/*!
 * \file HisDataReplayer.cpp
 * \project WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief 历史数据回放器实现文件
 * 
 * \details 本文件实现了历史数据回放器HisDataReplayer类的具体功能，
 *          包括历史K线数据、Tick数据、Level2数据的加载和回放，
 *          支持多种数据源格式（二进制文件、CSV文件、外部数据加载器），
 *          提供完整的回测数据基础设施功能。
 *          
 *          主要功能包括：
 *          - 历史数据的加载和缓存管理
 *          - K线数据、Tick数据、Level2数据的回放
 *          - 数据重采样和周期转换
 *          - 复权因子处理
 *          - 回测进度控制和状态管理
 *          - 多种回测模式支持（K线驱动、Tick驱动、任务驱动）
 */
#include "HisDataReplayer.h"
#include "EventNotifier.h"
#include "WtHelper.h"

#include <fstream>

#include "../Includes/WTSVariant.hpp"
#include "../Includes/WTSDataDef.hpp"
#include "../Includes/WTSContractInfo.hpp"
#include "../Includes/WTSSessionInfo.hpp"
#include "../Includes/WTSVariant.hpp"

#include "../Share/decimal.h"
#include "../Share/StrUtil.hpp"
#include "../Share/TimeUtils.hpp"

#include "../WTSTools/WTSLogger.h"
#include "../WTSTools/WTSDataFactory.h"
#include "../WTSTools/CsvHelper.h"

#include "../WTSUtils/WTSCmpHelper.hpp"
#include "../WTSUtils/WTSCfgLoader.h"

#include "../Share/CodeHelper.hpp"

#include <boost/filesystem.hpp>

#include <rapidjson/document.h>
#include <rapidjson/prettywriter.h>
namespace rj = rapidjson;

using namespace std;

/*!
 * \brief 处理数据块的函数
 * \details 该函数用于处理从文件中读取的数据块，包括解压缩、版本转换等操作。
 *          支持处理新旧版本的数据格式，自动进行数据结构升级和兼容性处理。
 * 
 * \param tag 数据标识，用于日志输出和错误定位
 * \param content 数据内容（输入输出参数），存储需要处理的原始数据，处理后更新为最终数据
 * \param isBar 是否为K线数据，用于区分处理K线数据还是Tick数据的转换逻辑
 * \param bKeepHead 是否保留数据块头部信息，默认为true
 * \return bool 处理是否成功，成功返回true，失败返回false
 * 
 * \note 支持功能：
 *       - 压缩数据的解压缩（基于WTSCmpHelper）
 *       - 旧版本数据结构向新版本的转换
 *       - WTSBarStructOld到WTSBarStruct的升级
 *       - WTSTickStructOld到WTSTickStruct的升级
 *       - 数据完整性检查和错误处理
 */
bool proc_block_data(const char* tag, std::string& content, bool isBar, bool bKeepHead = true)
{
	BlockHeader* header = (BlockHeader*)content.data();

	bool bCmped = header->is_compressed();
	bool bOldVer = header->is_old_version();

	// 如果没有压缩，也不是旧版本结构，则直接返回
	if (!bCmped && !bOldVer)
	{
		if (!bKeepHead)
			content.erase(0, BLOCK_HEADER_SIZE);
		return true;
	}

	std::string buffer;
	if (bCmped)
	{
		BlockHeaderV2* blkV2 = (BlockHeaderV2*)content.c_str();

		if (content.size() != (sizeof(BlockHeaderV2) + blkV2->_size))
		{
			WTSLogger::error_f("Size check failed while processing {} data of {}", isBar ? "bar" : "tick", tag);
			return false;
		}

		// 跳过文件头，对数据进行解压
		buffer = WTSCmpHelper::uncompress_data(content.data() + BLOCK_HEADERV2_SIZE, (uint32_t)blkV2->_size);
	}
	else
	{
		if (!bOldVer)
		{
			// 既不压缩也不是旧版本，直接返回
			if (!bKeepHead)
				content.erase(0, BLOCK_HEADER_SIZE);
			return true;
		}
		else
		{
			buffer.append(content.data() + BLOCK_HEADER_SIZE, content.size() - BLOCK_HEADER_SIZE);
		}
	}

	if (bOldVer)
	{
		if (isBar)
		{
			std::string bufV2;
			uint32_t barcnt = buffer.size() / sizeof(WTSBarStructOld);
			bufV2.resize(barcnt * sizeof(WTSBarStruct));
			WTSBarStruct* newBar = (WTSBarStruct*)bufV2.data();
			WTSBarStructOld* oldBar = (WTSBarStructOld*)buffer.data();
			for (uint32_t idx = 0; idx < barcnt; idx++)
			{
				newBar[idx] = oldBar[idx];
			}
			buffer.swap(bufV2);

			WTSLogger::debug_f("{} bars of {} transferd to new version...", barcnt, tag);
		}
		else
		{
			uint32_t tick_cnt = buffer.size() / sizeof(WTSTickStructOld);
			std::string bufv2;
			bufv2.resize(sizeof(WTSTickStruct)*tick_cnt);
			WTSTickStruct* newTick = (WTSTickStruct*)bufv2.data();
			WTSTickStructOld* oldTick = (WTSTickStructOld*)buffer.data();
			for (uint32_t i = 0; i < tick_cnt; i++)
			{
				newTick[i] = oldTick[i];
			}
			buffer.swap(bufv2);

			WTSLogger::debug_f("{} ticks of {} transferd to new version...", tick_cnt, tag);
		}
	}

	if (bKeepHead)
	{
		// 原来的缓存，resize成文件头大小，再追加解压的数据
		content.resize(BLOCK_HEADER_SIZE);
		content.append(buffer);

		// 修改数据块的版本号
		header = (BlockHeader*)content.data();
		header->_version = BLOCK_VERSION_RAW_V2;
	}
	else
	{
		// 不保留头部，直接和数据做一个swap
		content.swap(buffer);
	}

	return true;
}

/*!
 * \brief 历史数据回放器构造函数
 * \details 初始化各成员变量为默认值，包括监听器、时间参数、状态标志等。
 *          设置回测器的初始状态，为后续的数据回放做准备。
 * 
 * \note 初始化状态：
 *       - 回测监听器置空，等待外部设置
 *       - 当前时间和日期设为0，等待回测开始设置
 *       - Tick数据回放默认开启
 *       - 回测状态为未运行
 *       - 外部数据加载器置空
 */
HisDataReplayer::HisDataReplayer()
	: _listener(NULL)			// 回测事件监听器，由外部设置
	, _cur_date(0)				// 当前回测日期，回测开始时设置
	, _cur_time(0)				// 当前回测时间，回测开始时设置
	, _cur_secs(0)				// 当前回测秒数，精确到秒级
	, _cur_tdate(0)				// 当前交易日，基于交易日历计算
	, _tick_enabled(true)		// Tick数据回放开关，默认开启
	, _opened_tdate(0)			// 已开盘的交易日
	, _closed_tdate(0)			// 已收盘的交易日
	, _tick_simulated(true)		// Tick数据是否为模拟生成
	, _running(false)			// 回测运行状态标志
	, _begin_time(0)			// 回测开始时间
	, _end_time(0)				// 回测结束时间
	, _bt_loader(NULL)			// 外部数据加载器接口
{
}

/*!
 * \brief 历史数据回放器析构函数
 * \details 清理资源，释放内存。析构时自动清理所有缓存的数据和资源，
 *          包括K线数据缓存、Tick数据缓存、Level2数据缓存等。
 * 
 * \note 自动清理的资源：
 *       - 所有缓存的历史数据
 *       - 订阅映射表
 *       - 复权因子数据
 *       - 价格映射表等
 */
HisDataReplayer::~HisDataReplayer()
{
}

/*!
 * \brief 初始化历史数据回放器
 * \details 根据配置信息初始化数据回放器，完成回测环境的全面配置。
 *          包括数据源配置、时间范围设置、基础数据文件加载、复权因子加载等核心功能。
 * 
 * \param cfg 配置信息，包含数据路径、模式、时间范围、基础数据文件路径等配置项
 * \param notifier 事件通知器，用于向外部系统通知回测状态和进度信息
 * \param dataLoader 外部数据加载器，支持从外部系统加载历史数据
 * \return bool 初始化是否成功，成功返回true，失败返回false
 * 
 * \note 支持的数据模式：
 *       - storage: 使用WtDataStorage存储格式
 *       - bin: 使用二进制文件格式
 *       - wtp: 使用WonderTrader专有格式
 *       - csv: 使用CSV文本格式
 * 
 * \note 初始化流程：
 *       1. 解析配置参数（数据路径、模式、时间范围）
 *       2. 初始化历史数据管理器
 *       3. 加载基础数据文件（品种、合约、交易时段、节假日）
 *       4. 加载热门合约管理数据
 *       5. 加载手续费模板
 *       6. 加载股票复权因子数据
 */
bool HisDataReplayer::init(WTSVariant* cfg, EventNotifier* notifier /* = NULL */, IBtDataLoader* dataLoader /* = NULL */)
{
	_notifier = notifier;
	_bt_loader = dataLoader;

	_mode = cfg->getCString("mode");
	/*
	 *	By Wesley @ 2022.01.11
	 *	因为store可能会递归，所以做了一个数据迁移
	 *	如果有store就读取store的path，如果没有store就会获取root的path
	 */
	if (cfg->has("store"))
	{
		_base_dir = StrUtil::standardisePath(cfg->get("store")->getCString("path"));
	}
	else
	{
		_base_dir = StrUtil::standardisePath(cfg->getCString("path"));
	}
	
	if(_mode == "storage" || _mode == "bin" || _mode == "wtp")
	{
		if (cfg->has("store"))
		{
			_his_dt_mgr.init(cfg->get("store"));
		}
		else
		{
			WTSVariant* item = WTSVariant::createObject();
			item->append("path", _base_dir.c_str());
			_his_dt_mgr.init(item);
			item->release();
		}
	}
	
	bool isRangeCfg = (_begin_time == 0 || _end_time == 0);	// 是否从配置文件获取回测参数
	if(_begin_time == 0)
		_begin_time = cfg->getUInt64("stime");

	if(_end_time == 0)
		_end_time = cfg->getUInt64("etime");

	WTSLogger::info_f("Backtest time range is set to be [{},{}] via config", _begin_time, _end_time);

	_tick_enabled = cfg->getBoolean("tick");
	WTSLogger::info_f("Tick data replaying is {}", _tick_enabled ? "enabled" : "disabled");

	// 加载基础数据文件
	WTSVariant* cfgBF = cfg->get("basefiles");
	// 基础数据文件的编码，可以重写所以原配置文件中
	bool isUTF8 = cfgBF->getBoolean("utf-8");
	if (cfgBF->get("session"))
		_bd_mgr.loadSessions(cfgBF->getCString("session"), isUTF8);

	WTSVariant* cfgItem = cfgBF->get("commodity");
	if (cfgItem)
	{
		if (cfgItem->type() == WTSVariant::VT_String)
		{
			_bd_mgr.loadCommodities(cfgItem->asCString(), isUTF8);
		}
		else if (cfgItem->type() == WTSVariant::VT_Array)
		{
			for(uint32_t i = 0; i < cfgItem->size(); i ++)
			{
				_bd_mgr.loadCommodities(cfgItem->get(i)->asCString(), isUTF8);
			}
		}
	}

	cfgItem = cfgBF->get("contract");
	if (cfgItem)
	{
		if (cfgItem->type() == WTSVariant::VT_String)
		{
			_bd_mgr.loadContracts(cfgItem->asCString(), isUTF8);
		}
		else if (cfgItem->type() == WTSVariant::VT_Array)
		{
			for (uint32_t i = 0; i < cfgItem->size(); i++)
			{
				_bd_mgr.loadContracts(cfgItem->get(i)->asCString(), isUTF8);
			}
		}
	}

	if (cfgBF->get("holiday"))
		_bd_mgr.loadHolidays(cfgBF->getCString("holiday"));

	if (cfgBF->get("hot"))
		_hot_mgr.loadHots(cfgBF->getCString("hot"));

	if (cfgBF->get("second"))
		_hot_mgr.loadSeconds(cfgBF->getCString("second"));

	loadFees(cfg->getCString("fees"));

	/*
	 *	By Wesley @ 2021.12.20
	 *	先从extloader加载复权因子
	 *	如果加载失败，再从本地复权因子文件中加载复权因子文件
	 */
	bool bLoaded = loadStkAdjFactorsFromLoader();

	if (!bLoaded && cfg->has("adjfactor"))
		loadStkAdjFactorsFromFile(cfg->getCString("adjfactor"));

	return true;
}

/*!
 * \brief 从数据加载器加载股票复权因子
 * \details 通过外部数据加载器加载所有合约的复权因子数据。
 *          复权因子用于处理股票分红、送股、配股等除权除息行为，
 *          确保历史价格数据的连续性和可比性。
 * 
 * \return bool 加载是否成功，成功返回true，失败或无加载器返回false
 * 
 * \note 处理逻辑：
 *       - 调用外部数据加载器的loadAllAdjFactors方法
 *       - 为每个股票代码创建复权因子列表
 *       - 自动添加基准复权因子(19900101, 1.0)作为起始点
 *       - 按日期升序排序复权因子列表
 *       - 支持前复权和后复权的数据处理
 * 
 * \warning 如果没有设置外部数据加载器，将返回false
 */
bool HisDataReplayer::loadStkAdjFactorsFromLoader()
{
	if (NULL == _bt_loader)
		return false;

	bool ret = _bt_loader->loadAllAdjFactors(this, [](void* obj, const char* stdCode, uint32_t* dates, double* factors, uint32_t count) {
		HisDataReplayer* replayer = (HisDataReplayer*)obj;
		AdjFactorList& fctrLst = replayer->_adj_factors[stdCode];

		for (uint32_t i = 0; i < count; i++)
		{
			AdjFactor adjFact;
			adjFact._date = dates[i];
			adjFact._factor = factors[i];

			fctrLst.emplace_back(adjFact);
		}

		// 一定要把第一个加进去，不然最新前复权的基础因子可能会漏掉，造成数据错误
		AdjFactor adjFact;
		adjFact._date = 19900101;
		adjFact._factor = 1;
		fctrLst.emplace_back(adjFact);

		std::sort(fctrLst.begin(), fctrLst.end(), [](const AdjFactor& left, const AdjFactor& right) {
			return left._date < right._date;
		});
	});

	if (ret) WTSLogger::info_f("Adjusting factors of {} contracts loaded via extended loader", _adj_factors.size());
	return ret;
}

/*!
 * \brief 从文件加载股票复权因子
 * \details 从JSON/INI配置文件中加载股票复权因子数据。
 *          支持灵活的配置文件格式，可以处理不同的股票代码格式。
 * 
 * \param adjfile 复权因子文件路径，支持JSON和INI格式
 * \return bool 加载是否成功，成功返回true，失败返回false
 * 
 * \note 文件格式支持：
 *       - 支持带PID和不带PID的代码格式
 *       - 带PID格式: STK.600000 
 *       - 不带PID格式: 600000（自动补充为STK.600000）
 *       - 自动转换为标准格式: 交易所.品种.代码
 * 
 * \note 处理流程：
 *       1. 检查文件是否存在
 *       2. 使用WTSCfgLoader解析配置文件
 *       3. 遍历交易所和股票代码
 *       4. 解析每个股票的复权因子列表
 *       5. 添加基准复权因子并排序
 *       6. 统计加载结果并记录日志
 * 
 * \warning 如果文件不存在或解析失败，将记录错误日志并返回false
 */
bool HisDataReplayer::loadStkAdjFactorsFromFile(const char* adjfile)
{
	if (!StdFile::exists(adjfile))
	{
		 WTSLogger::error_f("Adjust factor file {} not exists, skipped", adjfile);
		return false;
	}

	std::string content;
	StdFile::read_file_content(adjfile, content);

	WTSVariant* doc = WTSCfgLoader::load_from_file(adjfile, true);
	if (doc == NULL)
	{
		WTSLogger::error_f("Parsing adjust factor file {} faield", adjfile);
		return false;
	}

	uint32_t stk_cnt = 0;
	uint32_t fct_cnt = 0;
	for (const std::string& exchg : doc->memberNames())
	{
		WTSVariant* itemExchg = doc->get(exchg);
		for (const std::string& code : itemExchg->memberNames())
		{
			WTSVariant* ayFacts = itemExchg->get(code);
			if (!ayFacts->isArray())
				continue;

			/*
			 *	By Wesley @ 2021.12.21
			 *	先检查code的格式是不是包含PID，如STK.600000
			 *	如果包含PID，则直接格式化，如果不包含，则当作STK
			 */
			bool bHasPID = (code.find('.') != std::string::npos);

			std::string key;
			if(bHasPID)
				key = fmt::format("{}.{}", exchg, code);
			else
				key = fmt::format("{}.STK.{}", exchg, code);
			stk_cnt++;

			AdjFactorList& fctrLst = _adj_factors[key];
			for (uint32_t i = 0; i < ayFacts->size(); i++)
			{
				WTSVariant* fItem = ayFacts->get(i);
				AdjFactor adjFact;
				adjFact._date = fItem->getUInt32("date");
				adjFact._factor = fItem->getDouble("factor");

				fctrLst.emplace_back(adjFact);
				fct_cnt++;
			}

			// 一定要把第一个加进去，不然最新前复权的基础因子可能会漏掉，造成数据错误
			AdjFactor adjFact;
			adjFact._date = 19900101;
			adjFact._factor = 1;
			fctrLst.emplace_back(adjFact);

			std::sort(fctrLst.begin(), fctrLst.end(), [](const AdjFactor& left, const AdjFactor& right) {
				return left._date < right._date;
			});
		}
	}

	WTSLogger::info_f("{} items of adjust factors for {} tickers loaded from {}", fct_cnt, stk_cnt, adjfile);
	doc->release();
	return true;
}


/*!
 * \brief 注册定时任务
 * \details 注册回测中的定时任务，支持多种触发周期的调度策略。
 *          定时任务用于在特定时间点触发策略执行，不依赖于K线或Tick数据。
 * 
 * \param taskid 任务ID，用于标识唯一的定时任务
 * \param date 触发日期，指定任务首次执行的基准日期
 * \param time 触发时间，指定任务执行的具体时间点（HHMM格式）
 * \param period 触发周期，支持的周期类型：
 *               - "d": 每日触发
 *               - "w": 每周触发  
 *               - "m": 每月触发
 *               - "y": 每年触发
 *               - "min": 分钟级触发
 * \param trdtpl 交易模板，指定使用的交易日历模板（默认"CHINA"）
 * \param session 交易时段，指定任务执行的交易时段（默认"TRADING"）
 * 
 * \note 任务调度特点：
 *       - 支持严格时间模式，确保任务在精确时间点执行
 *       - 考虑交易日历，自动跳过非交易日
 *       - 支持多种时间周期的灵活组合
 *       - 任务执行独立于数据回放进度
 */
void HisDataReplayer::register_task(uint32_t taskid, uint32_t date, uint32_t time, const char* period, const char* trdtpl /* = "CHINA" */, const char* session /* = "TRADING" */)
{
	TaskPeriodType ptype;
	if (wt_stricmp(period, "d") == 0)
		ptype = TPT_Daily;
	else if (wt_stricmp(period, "w") == 0)
		ptype = TPT_Weekly;
	else if (wt_stricmp(period, "m") == 0)
		ptype = TPT_Monthly;
	else if (wt_stricmp(period, "y") == 0)
		ptype = TPT_Yearly;
	else if (wt_stricmp(period, "min") == 0)
		ptype = TPT_Minute;
	else
		ptype = TPT_None;

	_task.reset(new TaskInfo);
	strcpy(_task->_name, "sel");
	strcpy(_task->_trdtpl, trdtpl);
	strcpy(_task->_session, session);
	_task->_day = date;
	_task->_time = time;
	_task->_id = taskid;
	_task->_period = ptype;
	_task->_strict_time = true;

	WTSLogger::info_f("Timed task registration succeed, frequency: {}", period);
}

/*!
 * \brief 清空所有数据缓存
 * \details 清空所有类型的历史数据缓存，释放内存空间。
 *          包括Tick数据、Level2数据（订单明细、订单队列、成交明细）、K线数据等。
 *          通常在回测结束或需要重新加载数据时调用。
 * 
 * \note 清空的缓存类型：
 *       - Tick数据缓存（_ticks_cache）
 *       - 订单明细缓存（_orddtl_cache）
 *       - 订单队列缓存（_ordque_cache） 
 *       - 成交明细缓存（_trans_cache）
 *       - K线数据缓存（_bars_cache）
 * 
 * \warning 清空缓存后，之前加载的所有历史数据将被释放，
 *          如需重新使用需要重新加载
 */
void HisDataReplayer::clear_cache()
{
	_ticks_cache.clear();		// 清空Tick数据缓存
	_orddtl_cache.clear();		// 清空订单明细缓存  
	_ordque_cache.clear();		// 清空订单队列缓存
	_trans_cache.clear();		// 清空成交明细缓存

	_bars_cache.clear();		// 清空K线数据缓存

	WTSLogger::log_raw(LL_WARN, "All cached data cleared");
}

/*!
 * \brief 重置回放器状态
 * \details 重置各类数据缓存的迭代器，清空订阅映射表，重置时间状态等，
 *          为新的回测运行做准备。不清空已加载的数据，只重置访问状态。
 * 
 * \note 重置的状态包括：
 *       - 所有数据缓存的游标位置（cursor）
 *       - 订阅映射表（tick、level2数据订阅）
 *       - 价格映射表
 *       - 主K线标识和最小周期
 *       - 当前时间状态
 *       - 开盘收盘状态
 */
void HisDataReplayer::reset()
{
	// 清空缓存，重置迭代器
	for(auto& m : _ticks_cache)
	{
		HftDataList<WTSTickStruct>& cacheItem = (HftDataList<WTSTickStruct>&)m.second;
		cacheItem._cursor = UINT_MAX;
	}

	for (auto& m : _orddtl_cache)
	{
		HftDataList<WTSOrdDtlStruct>& cacheItem = (HftDataList<WTSOrdDtlStruct>&)m.second;
		cacheItem._cursor = UINT_MAX;
	}

	for (auto& m : _ordque_cache)
	{
		HftDataList<WTSOrdQueStruct>& cacheItem = (HftDataList<WTSOrdQueStruct>&)m.second;
		cacheItem._cursor = UINT_MAX;
	}

	for (auto& m : _trans_cache)
	{
		HftDataList<WTSTransStruct>& cacheItem = (HftDataList<WTSTransStruct>&)m.second;
		cacheItem._cursor = UINT_MAX;
	}

	for (auto& m : _bars_cache)
	{
		BarsList& cacheItem = (BarsList&)m.second;
		cacheItem._cursor = UINT_MAX;

		WTSLogger::info_f("Reading flag of {} has been reset", m.first.c_str());
	}

	_unbars_cache.clear();

	_day_cache.clear();
	_ticker_keys.clear();

	_tick_sub_map.clear();
	_ordque_sub_map.clear();
	_orddtl_sub_map.clear();
	_trans_sub_map.clear();

	_price_map.clear();

	_main_key = "";
	_min_period = "";

	_cur_date = 0;
	_cur_time = 0;
	_cur_secs = 0;
	_cur_tdate = 0;
	_opened_tdate = 0;
	_closed_tdate = 0;
	_tick_simulated = true;
}

/*!
 * \brief 导出回测状态信息
 * \details 将回测的当前状态信息以JSON格式导出到文件，供外部监控系统使用。
 *          生成的状态文件包含完整的回测进度和性能指标，便于实时监控回测执行情况。
 * 
 * \param stdCode 合约代码，当前正在回测的主要合约标识
 * \param period K线周期类型（KP_DAY, KP_Minute1, KP_Minute5等）
 * \param times 周期倍数，与period结合确定具体的K线周期
 * \param stime 开始时间，回测数据的起始时间点
 * \param etime 结束时间，回测数据的结束时间点  
 * \param progress 回测进度，0.0-1.0之间的浮点数表示完成百分比
 * \param elapse 已耗时，从回测开始到当前的总耗时（纳秒）
 * 
 * \note 输出文件格式：
 *       - 文件路径: {输出目录}/{策略名}/btenv.json
 *       - JSON格式，包含code、period、stime、etime、progress、elapse字段
 *       - period字段格式化为易读的字符串（如"d"、"m1"、"m5"等）
 * 
 * \note 应用场景：
 *       - 外部监控系统实时获取回测进度
 *       - 回测性能分析和优化
 *       - 多个回测任务的状态管理
 */
void HisDataReplayer::dump_btstate(const char* stdCode, WTSKlinePeriod period, uint32_t times, uint64_t stime, uint64_t etime, double progress, int64_t elapse)
{
	std::string output;
	{
		rj::Document root(rj::kObjectType);
		rj::Document::AllocatorType &allocator = root.GetAllocator();

		root.AddMember("code", rj::Value(stdCode, allocator), allocator);

		std::stringstream ss;
		if (period == KP_DAY)
			ss << "d";
		else if (period == KP_Minute1)
			ss << "m" << times;
		else
			ss << "m" << times * 5;

		root.AddMember("period", rj::Value(ss.str().c_str(), allocator), allocator);
		
		root.AddMember("stime", stime, allocator);
		root.AddMember("etime", etime, allocator);
		root.AddMember("progress", progress, allocator);
		root.AddMember("elapse", elapse, allocator);

		rj::StringBuffer sb;
		rj::PrettyWriter<rj::StringBuffer> writer(sb);
		root.Accept(writer);

		output = sb.GetString();
	}

	std::string folder = WtHelper::getOutputDir();
	folder += _stra_name;
	folder += "/";
	boost::filesystem::create_directories(folder.c_str());
	std::string filename = folder + "btenv.json";
	StdFile::write_file_content(filename.c_str(), output.c_str(), output.size());
}

/*!
 * \brief 通知回测状态
 * \details 通过事件通知器向外部系统实时通知回测状态信息。
 *          与dump_btstate不同，此方法通过内存通道实时推送状态，
 *          适用于需要实时监控的场景。
 * 
 * \param stdCode 合约代码，当前正在回测的主要合约标识
 * \param period K线周期类型（KP_DAY, KP_Minute1, KP_Minute5等）
 * \param times 周期倍数，与period结合确定具体的K线周期
 * \param stime 开始时间，回测数据的起始时间点
 * \param etime 结束时间，回测数据的结束时间点
 * \param progress 回测进度，0.0-1.0之间的浮点数表示完成百分比
 * 
 * \note 通知机制：
 *       - 通过EventNotifier的notifyData方法推送
 *       - 事件类型为"BT_STATE"
 *       - 数据格式为JSON字符串
 *       - 仅在设置了事件通知器时才会执行
 * 
 * \note 与dump_btstate的区别：
 *       - notify_state: 内存推送，实时性好，适合UI更新
 *       - dump_btstate: 文件输出，持久化，适合离线分析
 * 
 * \warning 如果未设置EventNotifier，此方法将直接返回不执行任何操作
 */
void HisDataReplayer::notify_state(const char* stdCode, WTSKlinePeriod period, uint32_t times, uint64_t stime, uint64_t etime, double progress)
{
	if (!_notifier)
		return;

	std::string output;
	{
		rj::Document root(rj::kObjectType);
		rj::Document::AllocatorType &allocator = root.GetAllocator();

		root.AddMember("code", rj::Value(stdCode, allocator), allocator);

		std::stringstream ss;
		if (period == KP_DAY)
			ss << "d";
		else if (period == KP_Minute1)
			ss << "m" << times;
		else
			ss << "m" << times * 5;

		root.AddMember("period", rj::Value(ss.str().c_str(), allocator), allocator);

		root.AddMember("stime", stime, allocator);
		root.AddMember("etime", etime, allocator);
		root.AddMember("progress", progress, allocator);

		rj::StringBuffer sb;
		rj::PrettyWriter<rj::StringBuffer> writer(sb);
		root.Accept(writer);

		output = sb.GetString();
	}

	_notifier->notifyData("BT_STATE", (void*)output.c_str(), output.size());
}

/*!
 * \brief 定位K线索引位置  
 * \details 根据指定时间在K线数据中定位对应的索引位置。
 *          使用二分查找算法快速定位，支持日线和分钟线的时间匹配。
 * 
 * \param key K线数据缓存键值，格式为"合约代码#周期#倍数"
 * \param now 目标时间，格式为YYYYMMDDHHMM（如：202301011030）
 * \param bUpperBound 是否使用上界查找模式：
 *                    - false: 找到小于等于目标时间的最大索引
 *                    - true: 找到小于目标时间的最大索引
 * \return uint32_t 索引位置，如果未找到或数据为空返回UINT32_MAX
 * 
 * \note 查找算法特点：
 *       - 日线数据：基于date字段进行比较
 *       - 分钟线数据：基于time字段进行比较（内部时间格式）
 *       - 使用std::lower_bound实现O(log n)复杂度的二分查找
 *       - 支持边界处理，自动调整超出范围的索引
 * 
 * \note 时间格式说明：
 *       - 输入时间：YYYYMMDDHHMM（外部标准格式）
 *       - 内部时间：(date-19900000)*10000 + time（压缩格式）
 * 
 * \warning 如果指定的key不存在或数据为空，将返回UINT32_MAX
 */
uint32_t HisDataReplayer::locate_barindex(const std::string& key, uint64_t now, bool bUpperBound /* = false */)
{
	uint32_t curDate, curTime;
	curDate = (uint32_t)(now / 10000);
	curTime = (uint32_t)(now % 10000);

	BarsListPtr& barsList = _bars_cache[key];
	if (barsList == NULL)
		return UINT32_MAX;

	bool isDay = (barsList->_period == KP_DAY);

	WTSBarStruct bar;
	bar.date = curDate;
	bar.time = (curDate - 19900000) * 10000 + curTime;
	auto it = std::lower_bound(barsList->_bars.begin(), barsList->_bars.end(), bar, [isDay](const WTSBarStruct& a, const WTSBarStruct& b) {
		if (isDay)
			return a.date < b.date;
		else
			return a.time < b.time;
	});

	std::size_t idx;
	if (it == barsList->_bars.end())
		idx = barsList->_bars.size() - 1;
	else
	{
		if(bUpperBound)
		{// 如果超过边界，则需要比较时间，找到比当前时间小的K线
			if ((isDay && it->date > bar.date) || (!isDay && it->time > bar.time))
			{
				it--;
			}
		}

		 idx = it - barsList->_bars.begin();
	}
	
	return idx;
}

/*!
 * \brief 停止回测
 * \details 设置终止标志，使回测在下一个循环时优雅退出。
 *          采用协作式停止机制，不会立即中断正在执行的操作，
 *          而是等待当前数据处理完成后自然退出。
 * 
 * \note 停止机制：
 *       - 检查当前运行状态，如果未运行则记录错误
 *       - 设置_terminated标志为true
 *       - 回测主循环会检查此标志并在下一轮自动退出
 *       - 支持重复调用，不会产生副作用
 * 
 * \note 优雅停止的优点：
 *       - 保证数据完整性，不会中断关键操作
 *       - 允许策略完成当前计算周期
 *       - 确保回测状态的一致性
 * 
 * \warning 停止是异步的，调用后回测不会立即停止，
 *          需要等待当前处理周期完成
 */
/*!
 * \brief 停止回测运行
 * \details 优雅地停止正在运行的回测任务，设置终止标志位。
 *          回测主循环会在下一轮检查时发现终止标志并退出。
 * 
 * \note 停止机制：
 *       - 检查回测是否正在运行
 *       - 设置终止标志，等待主循环自然退出
 *       - 不会强制中断正在处理的数据
 *       - 确保数据处理的完整性
 * 
 * \warning 如果回测没有运行，会记录错误信息并直接返回
 */
void HisDataReplayer::stop()
{
	if(!_running)
	{
		WTSLogger::log_raw(LL_ERROR, "Backtesting is not running, no need to stop");
		return;
	}

	if (_terminated)
		return;

	_terminated = true;
	WTSLogger::log_raw(LL_WARN, "Terminating flag reset to true, backtesting will quit at next round");
}

/*!
 * \brief 准备回测环境
 * \details 初始化回测运行状态，完成回测前的所有准备工作。
 *          包括状态重置、时间初始化、策略初始化等关键步骤。
 * 
 * \return bool 准备是否成功，成功返回true，失败返回false
 * 
 * \note 准备流程：
 *       1. 检查运行状态，防止重复启动
 *       2. 设置运行标志和终止标志
 *       3. 重置所有数据缓存状态
 *       4. 初始化回测时间参数
 *       5. 计算初始交易日
 *       6. 通知回测开始事件
 *       7. 调用策略初始化
 *       8. 检查未订阅的K线数据
 * 
 * \note 安全检查：
 *       - 防止多个回测任务同时运行
 *       - 确保所有状态正确初始化
 *       - 提供完整的错误处理
 * 
 * \warning 如果已有回测任务在运行，将返回false并记录错误
 */
bool HisDataReplayer::prepare()
{
	if (_running)
	{
		WTSLogger::log_raw(LL_ERROR, "Cannot run more than one backtesting task at the same time");
		return false;
	}

	_running = true;
	_terminated = false;
	reset();

	_cur_date = (uint32_t)(_begin_time / 10000);
	_cur_time = (uint32_t)(_begin_time % 10000);
	_cur_secs = 0;
	_cur_tdate = _bd_mgr.calcTradingDate(DEFAULT_SESSIONID, _cur_date, _cur_time, true);

	if (_notifier)
		_notifier->notifyEvent("BT_START");

	_listener->handle_init();

	if (!_tick_enabled)
		checkUnbars();

	return true;
}

/*!
 * \brief 开始回测运行
 * \details 根据不同的配置模式启动回测：
 *          - 如果有定时任务，则按任务模式运行
 *          - 如果没有任务但有主K线，则按K线模式运行
 *          - 如果开启Tick模式，则按Tick模式运行
 * 
 * \param bNeedDump 是否需要导出状态信息（默认false）
 */
void HisDataReplayer::run(bool bNeedDump/* = false*/)
{
	if(_task == NULL)
	{
		// 如果没有任务，则进入K线回放模式

		// 如果没有主K线，则自动确定主K线
		if (_main_key.empty() && !_bars_cache.empty())
		{
			WTSKlinePeriod minPeriod = KP_DAY;
			uint32_t minTimes = 1;
			for(auto& m : _bars_cache)
			{
				const BarsListPtr& barsList = m.second;
				if (barsList->_period < minPeriod)
				{
					minPeriod = barsList->_period;
					minTimes = barsList->_times;
					_main_key = m.first;
				}
				else if(barsList->_period == minPeriod)
				{
					if(barsList->_times < minTimes)
					{
						_main_key = m.first;
						minTimes = barsList->_times;
					}
				}
			}

			WTSLogger::info_f("Main K bars automatic determined: {}", _main_key.c_str());
		}

		if(!_main_key.empty())
		{
			// 进入K线回放模式
			run_by_bars(bNeedDump);
		}
		else if(_tick_enabled)
		{
			run_by_ticks(bNeedDump);
		}
		else
		{
			WTSLogger::log_raw(LL_INFO, "Main K bars not subscribed and backtesting of tick data not available , replaying done");
			_listener->handle_replay_done();
			if (_notifier)
				_notifier->notifyEvent("BT_END");
		}
	}
	else //if(_task != NULL)
	{
		run_by_tasks(bNeedDump);
	}

	_running = false;
}

/*!
 * \brief 按Tick数据模式运行回测
 * \details 以Tick数据为驱动进行回测，逐个交易日处理Tick数据，
 *          适用于高频策略和精确时间回测场景
 * 
 * \param bNeedDump 是否需要导出状态信息（默认false）
 */
void HisDataReplayer::run_by_ticks(bool bNeedDump /* = false */)
{
	// 如果没有K线，则以tick数据为单位进行回放，每收到一个tick就回放一次
	uint32_t edt = (uint32_t)(_end_time / 10000);
	uint32_t etime = (uint32_t)(_end_time % 10000);
	uint64_t end_tdate = _bd_mgr.calcTradingDate(DEFAULT_SESSIONID, edt, etime, true);

	while (_cur_tdate <= end_tdate && !_terminated)
	{
		if (checkAllTicks(_cur_tdate))
		{
			WTSLogger::info_f("Start to replay tick data of {}...", _cur_tdate);
			_listener->handle_session_begin(_cur_tdate);
			replayHftDatasByDay(_cur_tdate);
			_listener->handle_session_end(_cur_tdate);
		}

		_cur_tdate = TimeUtils::getNextDate(_cur_tdate);
	}

	if (_terminated)
		WTSLogger::debug("Replaying by ticks terminated forcely");

	WTSLogger::log_raw(LL_INFO, "All back data replayed, replaying done");
	_listener->handle_replay_done();
	if (_notifier)
		_notifier->notifyEvent("BT_END");
}

/*!
 * \brief 按K线数据模式运行回测
 * \details 以主K线数据为驱动进行回测，逐根K线推进回测进度，
 *          支持进度监控和状态导出，适用于大部分策略回测场景
 * 
 * \param bNeedDump 是否需要导出状态信息（默认false）
 */
void HisDataReplayer::run_by_bars(bool bNeedDump /* = false */)
{
	TimeUtils::Ticker ticker;

	BarsListPtr barsList = _bars_cache[_main_key];
	WTSSessionInfo* sInfo = get_session_info(barsList->_code.c_str(), true);
	std::string commId = CodeHelper::stdCodeToStdCommID(barsList->_code.c_str());

	uint32_t sIdx = locate_barindex(_main_key, _begin_time, false);
	uint32_t eIdx = locate_barindex(_main_key, _end_time, true);

	uint32_t total_barcnt = eIdx - sIdx + 1;
	uint32_t replayed_barcnt = 0;

	notify_state(barsList->_code.c_str(), barsList->_period, barsList->_times, _begin_time, _end_time, 0);

	if (bNeedDump)
		dump_btstate(barsList->_code.c_str(), barsList->_period, barsList->_times, _begin_time, _end_time, 100.0, ticker.nano_seconds());

	WTSLogger::info_f("Start to replay back data from {}...", _begin_time);

	for (; !_terminated;)
	{
		bool isDay = barsList->_period == KP_DAY;
		if (barsList->_cursor != UINT_MAX)
		{
			uint64_t nextBarTime = 0;
			if (isDay)
				nextBarTime = (uint64_t)barsList->_bars[barsList->_cursor].date * 10000 + sInfo->getCloseTime();
			else
			{
				nextBarTime = (uint64_t)barsList->_bars[barsList->_cursor].time;
				nextBarTime += 199000000000;
			}

			if (nextBarTime > _end_time)
			{
				WTSLogger::info_f("{} is beyond ending time {},replaying done", nextBarTime, _end_time);
				break;
			}

			uint32_t nextDate = (uint32_t)(nextBarTime / 10000);
			uint32_t nextTime = (uint32_t)(nextBarTime % 10000);

			//By Wesley @ 2022.01.10
			// 如果当前时间是交易时间，则需要判断是否为交易日的交易时间
			// 如果是交易日的交易时间，则需要判断是否为交易日的交易时间
			uint32_t nextTDate = _opened_tdate;
			if(isDay || (!isDay && sInfo->offsetTime(nextTime, false) != sInfo->getCloseTime(true)))
			{
				nextTDate = _bd_mgr.calcTradingDate(commId.c_str(), nextDate, nextTime, false);
				if (_opened_tdate != nextTDate)
				{
					WTSLogger::debug_f("Tradingday {} begins", nextTDate);
					_listener->handle_session_begin(nextTDate);
					_opened_tdate = nextTDate;
					_cur_tdate = nextTDate;
				}
			}			

			uint64_t curBarTime = (uint64_t)_cur_date * 10000 + _cur_time;
			if (_tick_enabled)
			{
				// 如果tick数据可用，则直接使用tick数据
				// 如果tick数据不可用，则需要模拟tick数据
				_tick_simulated = !replayHftDatas(curBarTime, nextBarTime);
			}

			_cur_date = nextDate;
			_cur_time = nextTime;
			_cur_secs = 0;

			bool isEndTDate = (sInfo->offsetTime(_cur_time, false) >= sInfo->getCloseTime(true));

			if (!_tick_enabled)
			{
				checkUnbars();
				replayUnbars(curBarTime, nextBarTime, (isDay || isEndTDate) ? nextTDate : 0);
			}

			onMinuteEnd(nextDate, nextTime, (isDay || isEndTDate) ? nextTDate : 0, _tick_simulated);

			replayed_barcnt += 1;

			if (isEndTDate && _closed_tdate != _cur_tdate)
			{
				WTSLogger::debug_f("Tradingday {} ends", _cur_tdate);
				_listener->handle_session_end(_cur_tdate);
				_closed_tdate = _cur_tdate;
				_day_cache.clear();
			}

			notify_state(barsList->_code.c_str(), barsList->_period, barsList->_times, _begin_time, _end_time, replayed_barcnt*100.0 / total_barcnt);

			if (barsList->_cursor >= barsList->_bars.size())
			{
				WTSLogger::log_raw(LL_INFO, "All back data replayed, replaying done");
				break;
			}
		}
		else
		{
			WTSLogger::log_raw(LL_ERROR, "No back data initialized, replaying canceled");
			break;
		}
	}

	if (_terminated)
		WTSLogger::debug("Replaying by bars terminated forcely");

	notify_state(barsList->_code.c_str(), barsList->_period, barsList->_times, _begin_time, _end_time, 100);
	if (_notifier)
		_notifier->notifyEvent("BT_END");

	if (_closed_tdate != _cur_tdate)
	{
		WTSLogger::debug_f("Tradingday {} ends", _cur_tdate);
		_listener->handle_session_end(_cur_tdate);
	}

	if (bNeedDump)
	{
		dump_btstate(barsList->_code.c_str(), barsList->_period, barsList->_times, _begin_time, _end_time, 100.0, ticker.nano_seconds());
	}

	_listener->handle_replay_done();
}

/*!
 * @brief 按照任务频率进行历史数据回放
 * 
 * 根据预设的任务配置,按照指定的时间频率(日、周、月、年或分钟)进行数据回放
 * 支持复杂的调度逻辑,包括交易日判断、节假日处理、跨日处理等
 * 
 * @param bNeedDump 是否需要导出回测状态信息到文件
 */
void HisDataReplayer::run_by_tasks(bool bNeedDump /* = false */)
{
	// 以任务频率为单位进行回放，每达到任务触发条件就执行一次回放
	WTSSessionInfo* sInfo = NULL;
	const char* DEF_SESS = (strlen(_task->_session) == 0) ? DEFAULT_SESSIONID : _task->_session;
	sInfo = _bd_mgr.getSession(DEF_SESS);
	WTSLogger::info_f("Start to backtest with task frequency from {}...", _begin_time);

	// 从数据库中读取数据，并进行处理
	if (_task->_period != TPT_Minute)
	{
		uint32_t endtime = TimeUtils::getNextMinute(_task->_time, -1);
		bool bIsPreDay = endtime > _task->_time;
		if (bIsPreDay)
			_cur_date = TimeUtils::getNextDate(_cur_date, -1);

		for (; !_terminated;)
		{
			bool fired = false;
			// 获取上一个任务的执行时间
			uint32_t preTDate = TimeUtils::getNextDate(_cur_tdate, -1);
			if (_cur_time == endtime)
			{
				if (!_bd_mgr.isHoliday(_task->_trdtpl, _cur_date, true))
				{
					uint32_t weekDay = TimeUtils::getWeekDay(_cur_date);


					bool bHasHoliday = false;
					uint32_t days = 1;
					while (_bd_mgr.isHoliday(_task->_trdtpl, preTDate, true))
					{
						bHasHoliday = true;
						preTDate = TimeUtils::getNextDate(preTDate, -1);
						days++;
					}
					uint32_t preWD = TimeUtils::getWeekDay(preTDate);

					switch (_task->_period)
					{
					case TPT_Daily:
						fired = true;
						break;
					case TPT_Minute:
						break;
					case TPT_Monthly:
						//if (preTDate % 1000000 < _task->_day && _cur_date % 1000000 >= _task->_day)
						//	fired = true;
						if (_cur_date % 1000000 == _task->_day)
							fired = true;
						else if (bHasHoliday)
						{
							// 如果当前日期是节假日，则需要判断是否为节假日后的第一个交易日
							// 如果是节假日后的第一个交易日，则需要判断是否为节假日后的第一个交易日
							if ((preTDate % 10000 / 100 < _cur_date % 10000 / 100) && _cur_date % 1000000 > _task->_day)
							{
								fired = true;
							}
							else if (preTDate % 1000000 < _task->_day && _cur_date % 1000000 > _task->_day)
							{
								// 如果当前日期是节假日后的第一个交易日，则需要判断是否为节假日后的第一个交易日
								fired = true;
							}
						}
						break;
					case TPT_Weekly:
						//if (preWD < _task->_day && weekDay >= _task->_day)
						//	fired = true;
						if (weekDay == _task->_day)
							fired = true;
						else if (bHasHoliday)
						{
							if (days >= 7 && weekDay > _task->_day)
							{
								fired = true;
							}
							else if (preWD > weekDay && weekDay > _task->_day)
							{
								// 如果当前日期是节假日后的第一个交易日，则需要判断是否为节假日后的第一个交易日
								fired = true;
							}
							else if (preWD < _task->_day && weekDay > _task->_day)
							{
								fired = true;
							}
						}
						break;
					case TPT_Yearly:
						if (preTDate % 10000 < _task->_day && _cur_date % 10000 >= _task->_day)
							fired = true;
						break;
					}
				}
			}

			if (!fired)
			{
				// 如果当前时间小于任务频率，则需要等待下一个任务
				// 如果当前时间大于任务频率，则需要等待下一个任务
				if (_cur_time < endtime)
				{
					_cur_time = endtime;
					continue;
				}

				uint32_t newTDate = _bd_mgr.calcTradingDate(DEF_SESS, _cur_date, _cur_time, true);

				if (newTDate != _cur_tdate)
				{
					_cur_tdate = newTDate;
					if (_listener)
						_listener->handle_session_begin(newTDate);
					if (_listener)
						_listener->handle_session_end(newTDate);
				}
			}
			else
			{
				// 如果当前时间是任务频率，则需要判断是否为交易日的交易时间
				uint32_t curDate = _cur_date;
				uint32_t curTime = endtime;
				bool bEndSession = sInfo->offsetTime(curTime, true) >= sInfo->getCloseTime(true);
				if (_listener)
					_listener->handle_session_begin(_cur_tdate);
				onMinuteEnd(curDate, curTime, bEndSession ? _cur_tdate : preTDate);
				if (_listener)
					_listener->handle_session_end(_cur_tdate);
			}

			_cur_date = TimeUtils::getNextDate(_cur_date);
			_cur_time = endtime;
			_cur_tdate = _bd_mgr.calcTradingDate(DEF_SESS, _cur_date, _cur_time, true);

			uint64_t nextTime = (uint64_t)_cur_date * 10000 + _cur_time;
			if (nextTime > _end_time)
			{
				WTSLogger::log_raw(LL_INFO, "Backtesting with task frequency is done");
				if (_listener)
				{
					_listener->handle_session_end(_cur_tdate);
					_listener->handle_replay_done();
					if (_notifier)
						_notifier->notifyEvent("BT_END");
				}

				break;
			}
		}
	}
	else
	{
		if (_listener)
			_listener->handle_session_begin(_cur_tdate);

		for (; !_terminated;)
		{
			// 需要获取当前时间
			uint32_t mins = sInfo->timeToMinutes(_cur_time);
			// 如果当前时间不是任务频率的整数倍，则需要等待下一个任务频率
			if (mins % _task->_time != 0)
			{
				mins = mins / _task->_time + _task->_time;
				_cur_time = sInfo->minuteToTime(mins);
			}

			bool bNewTDate = false;
			if (mins < sInfo->getTradingMins())
			{
				onMinuteEnd(_cur_date, _cur_time, 0);
			}
			else
			{
				bNewTDate = true;
				mins = sInfo->getTradingMins();
				_cur_time = sInfo->getCloseTime();

				onMinuteEnd(_cur_date, _cur_time, _cur_tdate);

				if (_listener)
					_listener->handle_session_end(_cur_tdate);
			}


			if (bNewTDate)
			{
				// 如果当前时间是任务频率的整数倍，则需要判断是否为交易日的交易时间
				mins = _task->_time;
				uint32_t nextTDate = _bd_mgr.getNextTDate(_task->_trdtpl, _cur_tdate, 1, true);

				if (sInfo->getOffsetMins() != 0)
				{
					if (sInfo->getOffsetMins() > 0)
					{
						// 如果是实时数据，则需要判断是否为交易日的交易时间
						_cur_date = _cur_tdate;
						_cur_tdate = nextTDate;
					}
					else
					{
						// 如果是实时数据，则需要判断是否为交易日的交易时间
						_cur_tdate = nextTDate;
						_cur_date = _cur_tdate;
					}
				}

				_cur_time = sInfo->minuteToTime(mins);

				if (_listener)
					_listener->handle_session_begin(nextTDate);
			}
			else
			{
				mins += _task->_time;
				uint32_t newTime = sInfo->minuteToTime(mins);
				bool bNewDay = newTime < _cur_time;
				if (bNewDay)
					_cur_date = TimeUtils::getNextDate(_cur_date);

				uint32_t dayMins = _cur_time / 100 * 60 + _cur_time % 100;
				uint32_t nextDMins = newTime / 100 * 60 + newTime % 100;

				// 如果当前时间是任务频率的整数倍，则需要判断是否为交易日的交易时间
				bool bNewSec = (nextDMins - dayMins > _task->_time) && !bNewDay;

				while (bNewSec && _bd_mgr.isHoliday(_task->_trdtpl, _cur_date, true))
					_cur_date = TimeUtils::getNextDate(_cur_date);

				_cur_time = newTime;
			}

			uint64_t nextTime = (uint64_t)_cur_date * 10000 + _cur_time;
			if (nextTime > _end_time)
			{
				WTSLogger::log_raw(LL_INFO, "Backtesting with task frequency is done");
				if (_listener)
				{
					_listener->handle_session_end(_cur_tdate);
					_listener->handle_replay_done();
					if (_notifier)
						_notifier->notifyEvent("BT_END");
				}
				break;
			}
		}
	}
}

/*!
 * @brief 回放未完成的K线数据并转换为Tick数据
 * 
 * 将缓存中未完成的K线数据转换为模拟的Tick数据进行回放
 * 每根K线会生成4个Tick：开盘价、最高价、最低价、收盘价
 * 支持日线和非日线数据的不同处理逻辑
 * 
 * @param stime 开始时间(YYYYMMDD HHMM格式)
 * @param nowTime 当前时间(YYYYMMDD HHMM格式)
 * @param endTDate 结束交易日,默认为0
 */
void HisDataReplayer::replayUnbars(uint64_t stime, uint64_t nowTime, uint32_t endTDate /* = 0 */)
{
	uint32_t uDate = (uint32_t)(stime / 10000);

	for (auto& item : _unbars_cache)
	{
		BarsListPtr& barsList = (BarsListPtr&)item.second;
		if (barsList->_period != KP_DAY)
		{
			// 如果当前时间是交易时间，则需要判断是否为交易日的交易时间
			if (barsList->_bars.size() > barsList->_cursor)
			{
				for (;;)
				{
					WTSBarStruct& nextBar = barsList->_bars[barsList->_cursor];

					uint64_t barTime = 199000000000 + nextBar.time;
					if (barTime <= nowTime)
					{
						if(barTime <= stime)
							continue;

						// 如果当前时间是交易时间，则需要判断是否为交易日的交易时间
						WTSTickStruct& curTS = _day_cache[barsList->_code];
						strcpy(curTS.code, barsList->_code.c_str());
						curTS.action_date = _cur_date;
						curTS.action_time = _cur_time * 100000;

						curTS.price = nextBar.open;
						curTS.volume = nextBar.vol;

						// 如果当前时间是交易时间，则需要判断是否为交易日的交易时间
						if (decimal::eq(curTS.open, 0))
							curTS.open = curTS.price;
						curTS.high = max(curTS.price, curTS.high);
						if (decimal::eq(curTS.low, 0))
							curTS.low = curTS.price;
						else
							curTS.low = min(curTS.price, curTS.low);


						WTSTickData* curTick = WTSTickData::create(curTS);
						_listener->handle_tick(barsList->_code.c_str(), curTick);
						curTick->release();

						curTS.price = nextBar.high;
						curTS.volume = nextBar.vol;
						curTS.high = max(curTS.price, curTS.high);
						curTS.low = min(curTS.price, curTS.low);
						curTick = WTSTickData::create(curTS);
						_listener->handle_tick(barsList->_code.c_str(), curTick);
						curTick->release();

						curTS.price = nextBar.low;
						curTS.high = max(curTS.price, curTS.high);
						curTS.low = min(curTS.price, curTS.low);
						curTick = WTSTickData::create(curTS);
						_listener->handle_tick(barsList->_code.c_str(), curTick);
						curTick->release();

						curTS.price = nextBar.close;
						curTS.high = max(curTS.price, curTS.high);
						curTS.low = min(curTS.price, curTS.low);
						curTick = WTSTickData::create(curTS);
						_listener->handle_tick(barsList->_code.c_str(), curTick);
						curTick->release();
					}
					else
					{
						break;
					}

					barsList->_cursor++;

					if (barsList->_cursor == barsList->_bars.size())
						break;
				}
			}
		}
		else
		{
			if (barsList->_bars.size() > barsList->_cursor)
			{
				for (;;)
				{
					WTSBarStruct& nextBar = barsList->_bars[barsList->_cursor];

					if (nextBar.date <= endTDate)
					{
						if (nextBar.date <= uDate)
							continue;

						CodeHelper::CodeInfo cInfo = CodeHelper::extractStdCode(barsList->_code.c_str());
						WTSCommodityInfo* commInfo = _bd_mgr.getCommodity(cInfo._exchg, cInfo._product);

						std::string realCode = barsList->_code;
						if (commInfo->isStock() && cInfo.isExright())
							realCode = realCode.substr(0, realCode.size() - 1);

						WTSSessionInfo* sInfo = get_session_info(realCode.c_str(), true);
						uint32_t curTime = sInfo->getOpenTime();
						// 如果当前时间是交易时间，则需要判断是否为交易日的交易时间
						WTSTickStruct curTS;
						strcpy(curTS.code, realCode.c_str());
						curTS.action_date = _cur_date;
						curTS.action_time = curTime * 100000;

						curTS.price = nextBar.open;
						curTS.volume = nextBar.vol;
						WTSTickData* curTick = WTSTickData::create(curTS);
						_listener->handle_tick(realCode.c_str(), curTick);
						curTick->release();

						curTS.price = nextBar.high;
						curTS.volume = nextBar.vol;
						curTick = WTSTickData::create(curTS);
						_listener->handle_tick(realCode.c_str(), curTick);
						curTick->release();

						curTS.price = nextBar.low;
						curTick = WTSTickData::create(curTS);
						_listener->handle_tick(realCode.c_str(), curTick);
						curTick->release();

						curTS.price = nextBar.close;
						curTick = WTSTickData::create(curTS);
						_listener->handle_tick(realCode.c_str(), curTick);
						curTick->release();
					}
					else
					{
						break;
					}

					barsList->_cursor++;

					if (barsList->_cursor >= barsList->_bars.size())
						break;
				}
			}
		}
	}
}

/*!
 * @brief 获取下一个Tick数据的时间
 * 
 * 遍历所有订阅的合约,查找最近的下一个Tick数据时间
 * 用于确定下一次回放事件的触发时间点
 * 
 * @param curTDate 当前交易日
 * @param stime 起始时间,默认为UINT64_MAX表示从头开始
 * @return 下一个Tick时间,如果没有则返回UINT64_MAX
 */
uint64_t HisDataReplayer::getNextTickTime(uint32_t curTDate, uint64_t stime /* = UINT64_MAX */)
{
	uint64_t nextTime = UINT64_MAX;
	for (auto& v : _tick_sub_map)
	{
		const char* stdCode = v.first.c_str();
		if (!checkTicks(stdCode, curTDate))
			continue;

		WTSSessionInfo* sInfo = get_session_info(stdCode, true);

		auto& tickList = _ticks_cache[stdCode];
		if (tickList._cursor == UINT_MAX)
		{
			if (stime == UINT64_MAX)
			{
				/*
				 *	stime为UINT64_MAX
				 *	说明没有开始时间
				 *	需要确定一个开始时间
				 */
				for(tickList._cursor = 1; ; tickList._cursor++)
				{
					// 一直循环，直到找到一个交易时间
					if(tickList._cursor > tickList._count)
						break;

					uint32_t tickMin = tickList._items[tickList._cursor-1].action_time / 100000;
					if (sInfo->isInTradingTime(tickMin))
					{
						break;
					}
				}
			}
			else
			{
				uint32_t uDate = (uint32_t)(stime / 10000);
				uint32_t uTime = (uint32_t)(stime % 10000);

				WTSTickStruct curTick;
				curTick.action_date = uDate;
				curTick.action_time = uTime * 100000;

				auto tit = std::lower_bound(tickList._items.begin(), tickList._items.end(), curTick, [](const WTSTickStruct& a, const WTSTickStruct& b) {
					if (a.action_date != b.action_date)
						return a.action_date < b.action_date;
					else
						return a.action_time < b.action_time;
				});

				std::size_t idx = tit - tickList._items.begin();
				tickList._cursor = idx + 1;
			}
		}

		if (tickList._cursor >= tickList._count)
			continue;

		const WTSTickStruct& nextTick = tickList._items[tickList._cursor - 1];
		//By Wesley @ 2022.03.06
		// 如果当前时间是交易时间，则需要判断是否为交易时间的交易时间
		if(!sInfo->isInTradingTime(nextTick.action_time/100000))
			continue;
		uint64_t lastTime = (uint64_t)nextTick.action_date * 1000000000 + nextTick.action_time;

		nextTime = min(lastTime, nextTime);
	}

	return nextTime;
}

/*!
 * @brief 获取下一个成交明细数据的时间
 * 
 * 遍历所有订阅的成交明细合约,查找最近的下一个成交明细数据时间
 * 用于高频数据回放中确定下一次成交明细事件的触发时间点
 * 
 * @param curTDate 当前交易日
 * @param stime 起始时间,默认为UINT64_MAX表示从头开始
 * @return 下一个成交明细时间,如果没有则返回UINT64_MAX
 */
uint64_t HisDataReplayer::getNextTransTime(uint32_t curTDate, uint64_t stime /* = UINT64_MAX */)
{
	uint64_t nextTime = UINT64_MAX;
	for (auto v : _trans_sub_map)
	{
		std::string stdCode = v.first;
		if (!checkTransactions(stdCode.c_str(), curTDate))
			continue;

		auto& itemList = _trans_cache[stdCode];
		if (itemList._cursor == UINT_MAX)
		{
			if (stime == UINT64_MAX)
				itemList._cursor = 1;
			else
			{
				uint32_t uDate = (uint32_t)(stime / 10000);
				uint32_t uTime = (uint32_t)(stime % 10000);

				WTSTransStruct curItem;
				curItem.action_date = uDate;
				curItem.action_time = uTime * 100000;

				auto tit = std::lower_bound(itemList._items.begin(), itemList._items.end(), curItem, [](const WTSTransStruct& a, const WTSTransStruct& b) {
					if (a.action_date != b.action_date)
						return a.action_date < b.action_date;
					else
						return a.action_time < b.action_time;
				});

				std::size_t idx = tit - itemList._items.begin();
				itemList._cursor = idx + 1;
			}
		}

		if (itemList._cursor >= itemList._count)
			continue;

		const auto& nextItem = itemList._items[itemList._cursor - 1];
		uint64_t lastTime = (uint64_t)nextItem.action_date * 1000000000 + nextItem.action_time;

		nextTime = min(lastTime, nextTime);
	}

	return nextTime;
}

/*!
 * @brief 获取下一个委托明细数据的时间
 * 
 * 遍历所有订阅的委托明细合约,查找最近的下一个委托明细数据时间
 * 用于高频数据回放中确定下一次委托明细事件的触发时间点
 * 
 * @param curTDate 当前交易日
 * @param stime 起始时间,默认为UINT64_MAX表示从头开始
 * @return 下一个委托明细时间,如果没有则返回UINT64_MAX
 */
uint64_t HisDataReplayer::getNextOrdDtlTime(uint32_t curTDate, uint64_t stime /* = UINT64_MAX */)
{
	uint64_t nextTime = UINT64_MAX;
	for (auto v : _orddtl_sub_map)
	{
		std::string stdCode = v.first;
		if (!checkOrderDetails(stdCode.c_str(), curTDate))
			continue;

		auto& itemList = _orddtl_cache[stdCode];
		if (itemList._cursor == UINT_MAX)
		{
			if (stime == UINT64_MAX)
				itemList._cursor = 1;
			else
			{
				uint32_t uDate = (uint32_t)(stime / 10000);
				uint32_t uTime = (uint32_t)(stime % 10000);

				WTSOrdDtlStruct curItem;
				curItem.action_date = uDate;
				curItem.action_time = uTime * 100000;

				auto tit = std::lower_bound(itemList._items.begin(), itemList._items.end(), curItem, [](const WTSOrdDtlStruct& a, const WTSOrdDtlStruct& b) {
					if (a.action_date != b.action_date)
						return a.action_date < b.action_date;
					else
						return a.action_time < b.action_time;
				});

				std::size_t idx = tit - itemList._items.begin();
				itemList._cursor = idx + 1;
			}
		}

		if (itemList._cursor >= itemList._count)
			continue;

		const auto& nextItem = itemList._items[itemList._cursor - 1];
		uint64_t lastTime = (uint64_t)nextItem.action_date * 1000000000 + nextItem.action_time;

		nextTime = min(lastTime, nextTime);
	}

	return nextTime;
}

/*!
 * @brief 获取下一个委托队列数据的时间
 * 
 * 遍历所有订阅的委托队列合约,查找最近的下一个委托队列数据时间
 * 用于高频数据回放中确定下一次委托队列事件的触发时间点
 * 
 * @param curTDate 当前交易日
 * @param stime 起始时间,默认为UINT64_MAX表示从头开始
 * @return 下一个委托队列时间,如果没有则返回UINT64_MAX
 */
uint64_t HisDataReplayer::getNextOrdQueTime(uint32_t curTDate, uint64_t stime /* = UINT64_MAX */)
{
	uint64_t nextTime = UINT64_MAX;
	for (auto v : _ordque_sub_map)
	{
		std::string stdCode = v.first;
		if (!checkOrderQueues(stdCode.c_str(), curTDate))
			continue;

		auto& itemList = _ordque_cache[stdCode];
		if (itemList._cursor == UINT_MAX)
		{
			if (stime == UINT64_MAX)
				itemList._cursor = 1;
			else
			{
				uint32_t uDate = (uint32_t)(stime / 10000);
				uint32_t uTime = (uint32_t)(stime % 10000);

				WTSOrdQueStruct curItem;
				curItem.action_date = uDate;
				curItem.action_time = uTime * 100000;

				auto tit = std::lower_bound(itemList._items.begin(), itemList._items.end(), curItem, [](const WTSOrdQueStruct& a, const WTSOrdQueStruct& b) {
					if (a.action_date != b.action_date)
						return a.action_date < b.action_date;
					else
						return a.action_time < b.action_time;
				});

				std::size_t idx = tit - itemList._items.begin();
				itemList._cursor = idx + 1;
			}
		}

		if (itemList._cursor >= itemList._count)
			continue;

		const auto& nextItem = itemList._items[itemList._cursor - 1];
		uint64_t lastTime = (uint64_t)nextItem.action_date * 1000000000 + nextItem.action_time;

		nextTime = min(lastTime, nextTime);
	}

	return nextTime;
}

/*!
 * @brief 按日回放高频数据
 * 
 * 按时间顺序回放指定交易日的所有高频数据,包括Tick、委托明细、委托队列、成交明细
 * 所有数据按照实际发生的时间戳进行统一排序和回放,确保数据时序的正确性
 * 
 * @param curTDate 要回放的交易日
 * @return 回放的总Tick数量
 */
uint64_t HisDataReplayer::replayHftDatasByDay(uint32_t curTDate)
{
	uint64_t total_ticks = 0;
	for (;!_terminated;)
	{
		// 获取下一个tick的时间
		uint64_t nextTime = min(UINT64_MAX, getNextTickTime(curTDate));
		nextTime = min(nextTime, getNextOrdDtlTime(curTDate));
		nextTime = min(nextTime, getNextOrdQueTime(curTDate));
		nextTime = min(nextTime, getNextTransTime(curTDate));

		if(nextTime == UINT64_MAX)
			break;

		/*
		 *	By Wesley @ 2022.03.06
		 *	由于数据加载器的cursor会自动递增，所以需要手动递增
		 *	这个操作是真实的
		 */

		// 获取下一个tick的时间
		_cur_date = (uint32_t)(nextTime / 1000000000);
		_cur_time = nextTime % 1000000000 / 100000;
		_cur_secs = nextTime % 100000;

		// 1 获取下一个tick的数据
		for (auto& v : _orddtl_sub_map)
		{
			const char* stdCode = v.first.c_str();
			auto& itemList = _orddtl_cache[stdCode];
			//By Wesley @ 2022.03.06 
			// 如果数据为空，则跳过
			// 如果数据为空，则需要跳过
			if (itemList._items.empty() || itemList._cursor > itemList._count)
				continue;

			auto& nextItem = itemList._items[itemList._cursor - 1];
			uint64_t lastTime = (uint64_t)nextItem.action_date * 1000000000 + nextItem.action_time;
			if (lastTime <= nextTime)
			{
				itemList._cursor++;

				WTSOrdDtlData* newData = WTSOrdDtlData::create(nextItem);
				newData->setCode(stdCode);
				_listener->handle_order_detail(stdCode, newData);
				newData->release();

				total_ticks++;
			}
		}

		// 2 获取下一个交易数据
		for (auto& v : _trans_sub_map)
		{
			const char* stdCode = v.first.c_str();
			auto& itemList = _trans_cache[stdCode];
			//By Wesley @ 2022.03.06 
			// 如果数据为空，则跳过
			// 如果数据为空，则需要跳过
			if (itemList._items.empty() || itemList._cursor > itemList._count)
				continue;

			auto& nextItem = itemList._items[itemList._cursor - 1];
			uint64_t lastTime = (uint64_t)nextItem.action_date * 1000000000 + nextItem.action_time;
			if (lastTime <= nextTime)
			{
				itemList._cursor++;

				WTSTransData* newData = WTSTransData::create(nextItem);
				newData->setCode(stdCode);
				_listener->handle_transaction(stdCode, newData);
				newData->release();
				
				total_ticks++;
			}
		}

		// 3 获取下一个tick数据
		for (auto& v : _tick_sub_map)
		{
			//std::string stdCode = v.first;
			const char* stdCode = v.first.c_str();
			HftDataList<WTSTickStruct>& tickList = _ticks_cache[stdCode];
			//By Wesley @ 2022.03.06 
			// 如果数据为空，则跳过
			// 如果数据为空，则需要跳过
			if(tickList._items.empty() || tickList._cursor > tickList._count)
				continue;

			WTSTickStruct& nextTick = tickList._items[tickList._cursor - 1];
			uint64_t lastTime = (uint64_t)nextTick.action_date * 1000000000 + nextTick.action_time;
			if (lastTime <= nextTime)
			{
				tickList._cursor++;

				update_price(stdCode, nextTick.price);
				WTSTickData* newTick = WTSTickData::create(nextTick);
				newTick->setCode(stdCode);
				_listener->handle_tick(stdCode, newTick);
				newTick->release();
				
				total_ticks++;
			}
		}
		
		// 4 获取下一个订单数据
		for (auto& v : _ordque_sub_map)
		{
			const char* stdCode = v.first.c_str();
			auto& itemList = _ordque_cache[stdCode];
			//By Wesley @ 2022.03.06 
			// 如果数据为空，则跳过
			// 如果数据为空，则需要跳过
			if (itemList._items.empty() || itemList._cursor > itemList._count)
				continue;

			auto& nextItem = itemList._items[itemList._cursor - 1];
			uint64_t lastTime = (uint64_t)nextItem.action_date * 1000000000 + nextItem.action_time;
			if (lastTime <= nextTime)
			{
				itemList._cursor++;

				WTSOrdQueData* newData = WTSOrdQueData::create(nextItem);
				newData->setCode(stdCode);
				_listener->handle_order_queue(stdCode, newData);
				newData->release();
				
				total_ticks++;
			}
		}
	}

	return total_ticks;
}

/*!
 * @brief 在指定时间段内回放高频数据
 * 
 * 在给定的时间范围内按时间顺序回放所有高频数据
 * 包括Tick、委托明细、委托队列、成交明细等数据的统一回放
 * 
 * @param stime 开始时间(YYYYMMDD HHMM格式)
 * @param etime 结束时间(YYYYMMDD HHMM格式)
 * @return 是否成功回放完成
 */
bool HisDataReplayer::replayHftDatas(uint64_t stime, uint64_t etime)
{	
	for (;;)
	{
		uint64_t nextTime = min(UINT64_MAX, getNextTickTime(_cur_tdate, stime));
		if (nextTime == UINT64_MAX)
			return false;

		nextTime = min(nextTime, getNextOrdDtlTime(_cur_tdate, stime));
		nextTime = min(nextTime, getNextOrdQueTime(_cur_tdate, stime));
		nextTime = min(nextTime, getNextTransTime(_cur_tdate, stime));

		if (nextTime/100000 >= etime)
			break;

		_cur_date = (uint32_t)(nextTime / 1000000000);
		_cur_time = nextTime % 1000000000 / 100000;
		_cur_secs = nextTime % 100000;
		
		// 1 获取下一个tick的数据
		for (auto& v : _orddtl_sub_map)
		{
			const char* stdCode = v.first.c_str();
			auto& itemList = _orddtl_cache[stdCode];
			if (itemList._cursor > itemList._count)
				continue;

			auto& nextItem = itemList._items[itemList._cursor - 1];
			uint64_t lastTime = (uint64_t)nextItem.action_date * 1000000000 + nextItem.action_time;
			if (lastTime <= nextTime)
			{
				WTSOrdDtlData* newData = WTSOrdDtlData::create(nextItem);
				newData->setCode(stdCode);
				_listener->handle_order_detail(stdCode, newData);
				newData->release();

				itemList._cursor++;
			}
		}

		// 2 获取下一个交易数据
		for (auto& v : _trans_sub_map)
		{
			const char* stdCode = v.first.c_str();
			auto& itemList = _trans_cache[stdCode];
			if (itemList._cursor = itemList._count)
				continue;

			auto& nextItem = itemList._items[itemList._cursor - 1];
			uint64_t lastTime = (uint64_t)nextItem.action_date * 1000000000 + nextItem.action_time;
			if (lastTime <= nextTime)
			{
				WTSTransData* newData = WTSTransData::create(nextItem);
				newData->setCode(stdCode);
				_listener->handle_transaction(stdCode, newData);
				newData->release();

				itemList._cursor++;
			}
		}

		// 3 获取下一个tick数据
		for (auto& v : _tick_sub_map)
		{
			const char* stdCode = v.first.c_str();
			auto& itemList = _ticks_cache[stdCode];
			if (itemList._cursor > itemList._count)
				continue;

			auto& nextItem = itemList._items[itemList._cursor - 1];
			uint64_t lastTime = (uint64_t)nextItem.action_date * 1000000000 + nextItem.action_time;
			if (lastTime <= nextTime)
			{
				update_price(stdCode, nextItem.price);
				WTSTickData* newData = WTSTickData::create(nextItem);
				newData->setCode(stdCode);
				_listener->handle_tick(stdCode, newData);
				newData->release();

				itemList._cursor++;
			}
		}

		// 4 获取下一个订单数据
		for (auto& v : _ordque_sub_map)
		{
			const char* stdCode = v.first.c_str();
			auto& itemList = _ordque_cache[stdCode];
			if (itemList._cursor > itemList._count)
				continue;

			auto& nextItem = itemList._items[itemList._cursor - 1];
			uint64_t lastTime = (uint64_t)nextItem.action_date * 1000000000 + nextItem.action_time;
			if (lastTime <= nextTime)
			{
				WTSOrdQueData* newData = WTSOrdQueData::create(nextItem);
				newData->setCode(stdCode);
				_listener->handle_order_queue(stdCode, newData);
				newData->release();

				itemList._cursor++;
			}
		}
	}

	return true;
}

/*!
 * @brief 分钟结束事件处理
 * 
 * 处理每分钟结束时的逻辑,包括K线数据的回放和Tick数据的模拟生成
 * 根据K线数据模拟生成Tick数据(开高低收四个价格点)
 * 支持复权处理和因子调整
 * 
 * @param uDate 日期(YYYYMMDD格式)
 * @param uTime 时间(HHMM格式)
 * @param endTDate 结束交易日,默认为0
 * @param tickSimulated 是否模拟生成Tick数据,默认为true
 */
void HisDataReplayer::onMinuteEnd(uint32_t uDate, uint32_t uTime, uint32_t endTDate /* = 0 */, bool tickSimulated /* = true */)
{
	// 处理当前时间对应的K线数据
	uint64_t nowTime = (uint64_t)uDate * 10000 + uTime;

	for (auto it = _bars_cache.begin(); it != _bars_cache.end(); it++)
	{
		BarsListPtr& barsList = (BarsListPtr&)it->second;
		double factor = barsList->_factor;
		if (barsList->_period != KP_DAY)
		{
			//如果历史数据指针不在末尾, 说明是回测模式, 要重新调用历史数据
			if (barsList->_bars.size() > barsList->_cursor)
			{
				for (;;)
				{
					WTSBarStruct& nextBar = barsList->_bars[barsList->_cursor];

					uint64_t barTime = 199000000000 + nextBar.time;
					if (barTime <= nowTime)
					{
						//if (_task == NULL)
						{
							if (tickSimulated)
							{
								const std::string& ticker = _ticker_keys[barsList->_code];
								if (ticker == it->first)
								{
									//生成Tick
									WTSTickStruct& curTS = _day_cache[barsList->_code];
									strcpy(curTS.code, barsList->_code.c_str());
									curTS.action_date = _cur_date;
									curTS.action_time = _cur_time * 100000;

									curTS.price = nextBar.open / factor;
									curTS.volume = nextBar.vol;

									//更新K线的各个字段
									if (decimal::eq(curTS.open, 0))
										curTS.open = curTS.price;
									curTS.high = max(curTS.price, curTS.high);
									if (decimal::eq(curTS.low, 0))
										curTS.low = curTS.price;
									else
										curTS.low = min(curTS.price, curTS.low);

									update_price(barsList->_code.c_str(), curTS.price);
									WTSTickData* curTick = WTSTickData::create(curTS);
									_listener->handle_tick(barsList->_code.c_str(), curTick);
									curTick->release();

									curTS.price = nextBar.high / factor;
									curTS.volume = nextBar.vol;
									curTS.high = max(curTS.price, curTS.high);
									curTS.low = min(curTS.price, curTS.low);
									update_price(barsList->_code.c_str(), curTS.price);
									curTick = WTSTickData::create(curTS);
									_listener->handle_tick(barsList->_code.c_str(), curTick);
									curTick->release();

									curTS.price = nextBar.low / factor;
									curTS.high = max(curTS.price, curTS.high);
									curTS.low = min(curTS.price, curTS.low);
									update_price(barsList->_code.c_str(), curTS.price);
									curTick = WTSTickData::create(curTS);
									_listener->handle_tick(barsList->_code.c_str(), curTick);
									curTick->release();

									curTS.price = nextBar.close / factor;
									curTS.high = max(curTS.price, curTS.high);
									curTS.low = min(curTS.price, curTS.low);
									update_price(barsList->_code.c_str(), curTS.price);
									curTick = WTSTickData::create(curTS);
									_listener->handle_tick(barsList->_code.c_str(), curTick);
								}
							}

							uint32_t times = barsList->_times;
							if (barsList->_period == KP_Minute5)
								times *= 5;
							_listener->handle_bar_close(barsList->_code.c_str(), "m", times, &nextBar);
						}
					}
					else
					{
						break;
					}

					barsList->_cursor++;

					if (barsList->_cursor == barsList->_bars.size())
						break;
				}
			}
		}
		else
		{
			if (barsList->_bars.size() > barsList->_cursor)
			{
				for (;;)
				{
					WTSBarStruct& nextBar = barsList->_bars[barsList->_cursor];

					if (nextBar.date <= endTDate)
					{
						//if (_task == NULL)
						{
							if (tickSimulated)
							{
								const std::string& ticker = _ticker_keys[barsList->_code];
								if (ticker == it->first)
								{
									CodeHelper::CodeInfo cInfo = CodeHelper::extractStdCode(barsList->_code.c_str());
									WTSCommodityInfo* commInfo = _bd_mgr.getCommodity(cInfo._exchg, cInfo._product);

									std::string realCode = barsList->_code;
									if (commInfo->isStock() && cInfo._exright)
										realCode = realCode.substr(0, realCode.size() - 1);

									WTSSessionInfo* sInfo = get_session_info(realCode.c_str(), true);
									uint32_t curTime = sInfo->getCloseTime();
									//生成Tick
									WTSTickStruct curTS;
									strcpy(curTS.code, realCode.c_str());
									curTS.action_date = _cur_date;
									curTS.action_time = curTime * 100000;

									curTS.price = nextBar.open / factor;
									curTS.volume = nextBar.vol;
									update_price(barsList->_code.c_str(), curTS.price);
									WTSTickData* curTick = WTSTickData::create(curTS);
									_listener->handle_tick(realCode.c_str(), curTick);
									curTick->release();

									curTS.price = nextBar.high / factor;
									curTS.volume = nextBar.vol;
									update_price(barsList->_code.c_str(), curTS.price);
									curTick = WTSTickData::create(curTS);
									_listener->handle_tick(realCode.c_str(), curTick);
									curTick->release();

									curTS.price = nextBar.low / factor;
									update_price(barsList->_code.c_str(), curTS.price);
									curTick = WTSTickData::create(curTS);
									_listener->handle_tick(realCode.c_str(), curTick);
									curTick->release();

									curTS.price = nextBar.close / factor;
									update_price(barsList->_code.c_str(), curTS.price);
									curTick = WTSTickData::create(curTS);
									_listener->handle_tick(realCode.c_str(), curTick);
								}
							}

							_listener->handle_bar_close(barsList->_code.c_str(), "d", barsList->_times, &nextBar);
						}
					}
					else
					{
						break;
					}

					barsList->_cursor++;

					if (barsList->_cursor >= barsList->_bars.size())
						break;
				}
			}
		}
	}

	if (_listener)
		_listener->handle_schedule(uDate, uTime);
}

/*!
 * \brief 获取K线数据切片
 * \details 根据合约代码、周期、数量等参数获取K线数据切片，支持从缓存或文件加载，
 *          支持数据重采样、主K线设置、最小周期确定等功能
 * 
 * \param stdCode 标准合约代码
 * \param period 周期类型（"m"-分钟，"d"-日线）
 * \param count 获取数量
 * \param times 周期倍数（默认1）
 * \param isMain 是否为主K线（默认false）
 * \return WTSKlineSlice* K线数据切片指针，失败返回NULL
 */
WTSKlineSlice* HisDataReplayer::get_kline_slice(const char* stdCode, const char* period, uint32_t count, uint32_t times /* = 1 */, bool isMain /* = false */)
{
	std::string key = StrUtil::printf("%s#%s#%u", stdCode, period, times);
	if (isMain)
		_main_key = key;

	//if(!_tick_enabled)
	//强制判断,主要为了防止没有tick数据,策略用第二主线
	{
		if(_ticker_keys.find(stdCode) == _ticker_keys.end())
			_ticker_keys[stdCode] = key;
		else
		{
			std::string oldKey = _ticker_keys[stdCode];
			oldKey = oldKey.substr(strlen(stdCode) + 1);
			if (strcmp(period, "m") == 0 && oldKey.at(0) == 'd')
			{
				_ticker_keys[stdCode] = key;
				_min_period = period;
			}
			else if (oldKey.at(0) == period[0] && times < strtoul(oldKey.substr(2, 1).c_str(), NULL, 10))
			{
				_ticker_keys[stdCode] = key;
				_min_period = period;
			}
		}
	}

	WTSKlinePeriod kp;
	uint32_t realTimes = times;
	uint32_t baseTimes = 1;
	if (strcmp(period, "m") == 0)
	{
		if(times % 5 == 0)
		{
			kp = KP_Minute5;
			baseTimes = 5;
			realTimes /= 5;
		}
		else
		{
			kp = KP_Minute1;
		}
	}
	else
		kp = KP_DAY;

	bool isDay = kp == KP_DAY;

	auto it = _bars_cache.find(key);
	bool bHasHisData = false;
	bool bHasCache = (it != _bars_cache.end());
	if (!bHasCache)
	{
		if (realTimes != 1)
		{
			std::string rawKey = StrUtil::printf("%s#%s#%u", stdCode, period, baseTimes);
			if (_bars_cache.find(rawKey) == _bars_cache.end())
			{
				/*
				 *	By Wesley @ 2021.12.20
				 *	先从extloader加载数据，如果不能则调用原来的历史数据存储引擎接口
				 */
				if(NULL != _bt_loader)
					bHasHisData = cacheFinalBarsFromLoader(rawKey, stdCode, kp);
				
				if(!bHasHisData)
				{
					if (_mode == "csv")
					{
						bHasHisData = cacheRawBarsFromCSV(rawKey, stdCode, kp);
					}
					else
					{
						bHasHisData = cacheRawBarsFromBin(rawKey, stdCode, kp);
					}
				}
				
			}
			else
			{
				bHasHisData = true;
			}
		}
		else
		{
			/*
			 *	By Wesley @ 2021.12.20
			 *	先从extloader加载数据，如果不能则调用原来的历史数据存储引擎接口
			 */
			if (NULL != _bt_loader)
			{
				bHasHisData = cacheFinalBarsFromLoader(key, stdCode, kp);
			}

			if(!bHasHisData)
			{
				if (_mode == "csv")
				{
					bHasHisData = cacheRawBarsFromCSV(key, stdCode, kp);
				}
				else
				{
					bHasHisData = cacheRawBarsFromBin(key, stdCode, kp);
				}
			}
			
		}
	}
	else
	{
		bHasHisData = true;
	}

	if (!bHasHisData)
		return NULL;

	WTSSessionInfo* sInfo = get_session_info(stdCode, true);
	if(sInfo == NULL)
	{
		WTSLogger::error_f("Cannot find corresponding session of {}", stdCode);
		return NULL;
	}

	bool isClosed = (sInfo->offsetTime(_cur_time, true) >= sInfo->getCloseTime(true));
	if (realTimes != 1 && !bHasCache)
	{	
		std::string rawKey = StrUtil::printf("%s#%s#%u", stdCode, period, baseTimes);
		BarsListPtr& rawBars = _bars_cache[rawKey];
		WTSKlineSlice* rawKline = WTSKlineSlice::create(stdCode, kp, realTimes, &rawBars->_bars[0], rawBars->_bars.size());
		rawKline->setCode(stdCode);

		static WTSDataFactory dataFact;
		WTSKlineData* kData = dataFact.extractKlineData(rawKline, kp, realTimes, sInfo, true);
		rawKline->release();

		if(kData)
		{
			_bars_cache[key].reset(new BarsList());
			BarsListPtr barsList = _bars_cache[key];
			barsList->_code = stdCode;
			barsList->_period = kp;
			barsList->_times = realTimes;
			barsList->_count = kData->size();
			barsList->_bars.swap(kData->getDataRef());
			kData->release();
			WTSLogger::info_f("{} resampled {}{} back kline of {} ready", barsList->_bars.size(), period, times, stdCode);
		}
		else
		{
			WTSLogger::error_f("Resampling {}{} back kline of {} failed", period, times, stdCode);
			return NULL;
		}
	}

	BarsListPtr& kBlkPair = _bars_cache[key];
	if(kBlkPair == NULL)
	{
		return NULL;
	}

	if (kBlkPair->_cursor == UINT_MAX)
	{
		//还没有计算初始位置
		WTSBarStruct bar;
		bar.date = _cur_tdate;
		if(kp != KP_DAY)
			bar.time = (_cur_date - 19900000) * 10000 + _cur_time;
		
		auto it = std::lower_bound(kBlkPair->_bars.begin(), kBlkPair->_bars.end(), bar, [isDay, isClosed](const WTSBarStruct& a, const WTSBarStruct& b){
			if (isDay)
				if (!isClosed)
					return a.date < b.date;
				else 
					return a.date <= b.date;
			else
				return a.time < b.time;
		});

		std::size_t eIdx = it - kBlkPair->_bars.begin();

		if (it != kBlkPair->_bars.end())
		{
			WTSBarStruct& curBar = *it;
			if (isDay)
			{
				if (curBar.date >= _cur_tdate && !isClosed)
				{
					if (eIdx > 0)
					{
						it--;
						eIdx--;
					}

				}
				else if (curBar.date > _cur_tdate && isClosed)
				{
					if (eIdx > 0)
					{
						it--;
						eIdx--;
					}

				}
			}
			else
			{
				if (curBar.time > bar.time)
				{
					if (eIdx > 0)
					{
						it--;
						eIdx--;
					}
				}
			}

			kBlkPair->_cursor = eIdx + 1;
		}
		else
		{
			return NULL;
		}
	}
	else
	{
		uint32_t curMin = (_cur_date - 19900000) * 10000 + _cur_time;
		if (isDay)
		{
			if (kBlkPair->_cursor <= kBlkPair->_count)
			{
				if(!isClosed)
				{
					while (kBlkPair->_bars[kBlkPair->_cursor - 1].date < _cur_tdate  && kBlkPair->_cursor < kBlkPair->_count && kBlkPair->_bars[kBlkPair->_cursor].date < _cur_tdate)
					{
						kBlkPair->_cursor++;
					}
				}
				else
				{
					while (kBlkPair->_bars[kBlkPair->_cursor - 1].date <= _cur_tdate  && kBlkPair->_cursor < kBlkPair->_count && kBlkPair->_bars[kBlkPair->_cursor].date <= _cur_tdate)
					{
						kBlkPair->_cursor++;
					}
				}
				

				if (kBlkPair->_bars[kBlkPair->_cursor - 1].date > _cur_tdate)
					kBlkPair->_cursor--;
			}
		}
		else
		{
			if (kBlkPair->_cursor <= kBlkPair->_count)
			{
				while (kBlkPair->_bars[kBlkPair->_cursor-1].time < curMin && kBlkPair->_cursor < kBlkPair->_count)
				{
					kBlkPair->_cursor++;
				}

				if (kBlkPair->_bars[kBlkPair->_cursor - 1].time > curMin)
					kBlkPair->_cursor--;
			}
		}
	}


	if (kBlkPair->_cursor == 0)
		return NULL;

	uint32_t sIdx = 0;
	if (kBlkPair->_cursor > count)
		sIdx = kBlkPair->_cursor - count;

	uint32_t realCnt = kBlkPair->_cursor - sIdx;
	WTSKlineSlice* kline = WTSKlineSlice::create(stdCode, kp, 1, kBlkPair->_bars.data() + sIdx, realCnt);
	return kline;
}


/*!
 * @brief 获取Tick数据切片
 * 
 * 获取指定合约的Tick数据切片,支持从缓存加载和指定结束时间
 * 主要用于策略获取历史Tick数据进行分析
 * 
 * @param stdCode 标准合约代码
 * @param count 获取的数据数量
 * @param etime 结束时间,默认为当前时间
 * @return WTSTickSlice* Tick数据切片指针,失败返回NULL
 */
WTSTickSlice* HisDataReplayer::get_tick_slice(const char* stdCode, uint32_t count, uint64_t etime)
{
	if (!_tick_enabled)
		return NULL;

	if (!checkTicks(stdCode, _cur_tdate))
		return NULL;

	auto& tickList = _ticks_cache[stdCode];
	if (tickList._cursor == 0)
		return NULL;

	if (tickList._cursor == UINT_MAX)
	{
		uint32_t uDate = _cur_date;
		uint32_t uTime = _cur_time * 100000 + _cur_secs;

		if (etime != 0)
		{
			uDate = (uint32_t)(etime / 10000);
			uTime = (uint32_t)(etime % 10000 * 100000);
		}

		WTSTickStruct curTick;
		curTick.action_date = uDate;
		curTick.action_time = uTime;

		auto tit = std::lower_bound(tickList._items.begin(), tickList._items.end(), curTick, [](const WTSTickStruct& a, const WTSTickStruct& b){
			if (a.action_date != b.action_date)
				return a.action_date < b.action_date;
			else
				return a.action_time < b.action_time;
		});

		if(tit == tickList._items.end())
		{
			tickList._cursor = tickList._items.size();
		}
		else
		{
			
			std::size_t idx = tit - tickList._items.begin();
			const WTSTickStruct& thisTick = *tit;
			if (thisTick.action_date > uDate || (thisTick.action_date == uDate && thisTick.action_time > uTime))
			{
				if(idx > 0)
				{
					tit--;
					idx--;
				}
				else
				{
					return NULL;
				}
			}

			tickList._cursor = idx + 1;
		}
	}
	
	//cursor表示下一个tick的index+1，相当于当前结束时间
	//所以要获取当前结束时间之前最后一个tick，需要-2
	uint32_t eIdx = tickList._cursor - 2;
	uint32_t sIdx = 0;
	if (eIdx >= count - 1)
		sIdx = eIdx + 1 - count;

	uint32_t realCnt = eIdx - sIdx + 1;
	if (realCnt == 0)
		return NULL;

	WTSTickSlice* ticks = WTSTickSlice::create(stdCode, tickList._items.data() + sIdx, realCnt);
	return ticks;
}

/*!
 * \brief 获取订单明细数据切片
 * \details 根据合约代码、数量、结束时间等参数获取订单明细数据切片，
 *          用于Level2数据分析
 * 
 * \param stdCode 标准合约代码
 * \param count 获取数量
 * \param etime 结束时间（默认为0，表示当前时间）
 * \return WTSOrdDtlSlice* 订单明细数据切片指针，失败返回NULL
 */
WTSOrdDtlSlice* HisDataReplayer::get_order_detail_slice(const char* stdCode, uint32_t count, uint64_t etime /* = 0 */)
{
	if (!checkOrderDetails(stdCode, _cur_tdate))
		return NULL;

	auto& dataList = _orddtl_cache[stdCode];
	if (dataList._cursor == 0)
		return NULL;

	if (dataList._cursor == UINT_MAX)
	{
		uint32_t uDate = _cur_date;
		uint32_t uTime = _cur_time * 100000 + _cur_secs;

		if (etime != 0)
		{
			uDate = (uint32_t)(etime / 10000);
			uTime = (uint32_t)(etime % 10000 * 100000);
		}

		WTSOrdDtlStruct curItem;
		curItem.action_date = uDate;
		curItem.action_time = uTime;

		auto tit = std::lower_bound(dataList._items.begin(), dataList._items.end(), curItem, [](const WTSOrdDtlStruct& a, const WTSOrdDtlStruct& b) {
			if (a.action_date != b.action_date)
				return a.action_date < b.action_date;
			else
				return a.action_time < b.action_time;
		});

		std::size_t idx = tit - dataList._items.begin();
		dataList._cursor = idx + 1;
	}

	uint32_t eIdx = dataList._cursor - 1;
	uint32_t sIdx = 0;
	if (eIdx >= count - 1)
		sIdx = eIdx + 1 - count;

	uint32_t realCnt = eIdx - sIdx + 1;
	if (realCnt == 0)
		return NULL;

	WTSOrdDtlSlice* ticks = WTSOrdDtlSlice::create(stdCode, dataList._items.data() + sIdx, realCnt);
	return ticks;
}

/*!
 * \brief 获取订单队列数据切片
 * \details 根据合约代码、数量、结束时间等参数获取订单队列数据切片，
 *          用于Level2订单队列分析
 * 
 * \param stdCode 标准合约代码
 * \param count 获取数量
 * \param etime 结束时间（默认为0，表示当前时间）
 * \return WTSOrdQueSlice* 订单队列数据切片指针，失败返回NULL
 */
WTSOrdQueSlice* HisDataReplayer::get_order_queue_slice(const char* stdCode, uint32_t count, uint64_t etime /* = 0 */)
{
	if (!checkOrderQueues(stdCode, _cur_tdate))
		return NULL;

	auto& dataList = _ordque_cache[stdCode];
	if (dataList._cursor == 0)
		return NULL;

	if (dataList._cursor == UINT_MAX)
	{
		uint32_t uDate = _cur_date;
		uint32_t uTime = _cur_time * 100000 + _cur_secs;

		if (etime != 0)
		{
			uDate = (uint32_t)(etime / 10000);
			uTime = (uint32_t)(etime % 10000 * 100000);
		}

		WTSOrdQueStruct curItem;
		curItem.action_date = uDate;
		curItem.action_time = uTime;

		auto tit = std::lower_bound(dataList._items.begin(), dataList._items.end(), curItem, [](const WTSOrdQueStruct& a, const WTSOrdQueStruct& b) {
			if (a.action_date != b.action_date)
				return a.action_date < b.action_date;
			else
				return a.action_time < b.action_time;
		});

		std::size_t idx = tit - dataList._items.begin();
		dataList._cursor = idx + 1;
	}

	uint32_t eIdx = dataList._cursor - 1;
	uint32_t sIdx = 0;
	if (eIdx >= count - 1)
		sIdx = eIdx + 1 - count;

	uint32_t realCnt = eIdx - sIdx + 1;
	if (realCnt == 0)
		return NULL;

	WTSOrdQueSlice* ticks = WTSOrdQueSlice::create(stdCode, dataList._items.data() + sIdx, realCnt);
	return ticks;
}

/*!
 * \brief 获取成交明细数据切片
 * \details 根据合约代码、数量、结束时间等参数获取成交明细数据切片，
 *          用于分析成交行为和市场微观结构
 * 
 * \param stdCode 标准合约代码
 * \param count 获取数量
 * \param etime 结束时间（默认为0，表示当前时间）
 * \return WTSTransSlice* 成交明细数据切片指针，失败返回NULL
 */
WTSTransSlice* HisDataReplayer::get_transaction_slice(const char* stdCode, uint32_t count, uint64_t etime /* = 0 */)
{
	if (!checkTransactions(stdCode, _cur_tdate))
		return NULL;

	auto& dataList = _trans_cache[stdCode];
	if (dataList._cursor == 0)
		return NULL;

	if (dataList._cursor == UINT_MAX)
	{
		uint32_t uDate = _cur_date;
		uint32_t uTime = _cur_time * 100000 + _cur_secs;

		if (etime != 0)
		{
			uDate = (uint32_t)(etime / 10000);
			uTime = (uint32_t)(etime % 10000 * 100000);
		}

		WTSTransStruct curItem;
		curItem.action_date = uDate;
		curItem.action_time = uTime;

		auto tit = std::lower_bound(dataList._items.begin(), dataList._items.end(), curItem, [](const WTSTransStruct& a, const WTSTransStruct& b) {
			if (a.action_date != b.action_date)
				return a.action_date < b.action_date;
			else
				return a.action_time < b.action_time;
		});

		std::size_t idx = tit - dataList._items.begin();
		dataList._cursor = idx + 1;
	}

	std::size_t eIdx = dataList._cursor - 1;
	std::size_t sIdx = 0;
	if (eIdx >= count - 1)
		sIdx = eIdx + 1 - count;

	std::size_t realCnt = eIdx - sIdx + 1;
	if (realCnt == 0)
		return NULL;

	WTSTransSlice* ticks = WTSTransSlice::create(stdCode, dataList._items.data() + sIdx, realCnt);
	return ticks;
}

bool HisDataReplayer::checkAllTicks(uint32_t uDate)
{
	bool bHasTick = false;
	for (auto& v : _tick_sub_map)
	{
		bHasTick = bHasTick || checkTicks(v.first.c_str(), uDate);
	}

	return bHasTick;
}

/*!
 * @brief 检查委托明细数据是否可用
 * 
 * 检查指定合约指定日期的委托明细数据是否已经缓存或可以加载
 * 如果数据未缓存,则尝试从数据源加载到内存中
 * 
 * @param stdCode 标准合约代码
 * @param uDate 日期(YYYYMMDD格式)
 * @return 数据是否可用
 */
bool HisDataReplayer::checkOrderDetails(const char* stdCode, uint32_t uDate)
{
	bool bNeedCache = false;
	auto it = _ticks_cache.find(stdCode);
	if (it == _ticks_cache.end())
		bNeedCache = true;
	else
	{
		auto& tickList = it->second;
		if (tickList._date != uDate)
			bNeedCache = true;
	}


	if (bNeedCache)
	{
		bool hasTicks = false;
		if(NULL != _bt_loader)
		{
			hasTicks = cacheRawTicksFromLoader(stdCode, stdCode, uDate);
		}
		else if (_mode == "csv")
		{
			hasTicks = cacheRawTicksFromCSV(stdCode, stdCode, uDate);
		}
		else
		{
			hasTicks = cacheRawTicksFromBin(stdCode, stdCode, uDate);
		}

		if (!hasTicks)
			return false;
	}

	return true;
}

/*!
 * @brief 检查委托队列数据是否可用
 * 
 * 检查指定合约指定日期的委托队列数据是否已经缓存或可以加载
 * 如果数据未缓存,则尝试从数据源加载到内存中
 * 
 * @param stdCode 标准合约代码
 * @param uDate 日期(YYYYMMDD格式)
 * @return 数据是否可用
 */
bool HisDataReplayer::checkOrderQueues(const char* stdCode, uint32_t uDate)
{
	bool bNeedCache = false;
	auto it = _ticks_cache.find(stdCode);
	if (it == _ticks_cache.end())
		bNeedCache = true;
	else
	{
		auto& tickList = it->second;
		if (tickList._date != uDate)
			bNeedCache = true;
	}


	if (bNeedCache)
	{
		bool hasTicks = false;
		if (NULL != _bt_loader)
		{
			hasTicks = cacheRawTicksFromLoader(stdCode, stdCode, uDate);
		}
		else if (_mode == "csv")
		{
			hasTicks = cacheRawTicksFromCSV(stdCode, stdCode, uDate);
		}
		else
		{
			hasTicks = cacheRawTicksFromBin(stdCode, stdCode, uDate);
		}

		if (!hasTicks)
			return false;
	}

	return true;
}

/*!
 * @brief 检查成交明细数据是否可用
 * 
 * 检查指定合约指定日期的成交明细数据是否已经缓存或可以加载
 * 如果数据未缓存,则尝试从数据源加载到内存中
 * 
 * @param stdCode 标准合约代码
 * @param uDate 日期(YYYYMMDD格式)
 * @return 数据是否可用
 */
bool HisDataReplayer::checkTransactions(const char* stdCode, uint32_t uDate)
{
	bool bNeedCache = false;
	auto it = _ticks_cache.find(stdCode);
	if (it == _ticks_cache.end())
		bNeedCache = true;
	else
	{
		auto& tickList = it->second;
		if (tickList._date != uDate)
			bNeedCache = true;
	}


	if (bNeedCache)
	{
		bool hasTicks = false;
		if (_mode == "csv")
		{
			hasTicks = cacheRawTicksFromCSV(stdCode, stdCode, uDate);
		}
		else
		{
			hasTicks = cacheRawTicksFromBin(stdCode, stdCode, uDate);
		}

		if (!hasTicks)
			return false;
	}

	return true;
}

/*!
 * @brief 检查Tick数据是否可用
 * 
 * 检查指定合约指定日期的Tick数据是否已经缓存或可以加载
 * 如果数据未缓存,则尝试从各种数据源(加载器、CSV文件、二进制文件)加载到内存中
 * 这是最常用的数据检查函数,支持多种数据源格式
 * 
 * @param stdCode 标准合约代码
 * @param uDate 日期(YYYYMMDD格式)
 * @return 数据是否可用
 */
bool HisDataReplayer::checkTicks(const char* stdCode, uint32_t uDate)
{
	if (strlen(stdCode) == 0)
		return false;

	bool bNeedCache = false;
	auto it = _ticks_cache.find(stdCode);
	if (it == _ticks_cache.end())
		bNeedCache = true;
	else
	{
		HftDataList<WTSTickStruct>& tickList = (HftDataList<WTSTickStruct>&)it->second;
		if (tickList._date != uDate)
		{
			tickList._items.clear();
			bNeedCache = true;
		}
		else if (tickList._count == 0)
			return false;
	}
	
	if (bNeedCache)
	{
		bool hasTicks = false;
		if (NULL != _bt_loader)
		{
			hasTicks = cacheRawTicksFromLoader(stdCode, stdCode, uDate);
		}
		else if (_mode == "csv")
		{
			hasTicks = cacheRawTicksFromCSV(stdCode, stdCode, uDate);
		}
		else
		{
			hasTicks = cacheRawTicksFromBin(stdCode, stdCode, uDate);
		}

		if (!hasTicks)
		{
			auto& ticksList = _ticks_cache[stdCode];
			ticksList._items.resize(0);
			ticksList._cursor = UINT_MAX;
			ticksList._code = stdCode;
			ticksList._date = uDate;
			ticksList._count = 0;
			return false;
		}
	}


	return true;
}

/*!
 * @brief 获取最新的Tick数据
 * 
 * 获取指定合约在当前回放时间点的最新Tick数据
 * 用于策略获取最新的价格和成交信息
 * 
 * @param stdCode 标准合约代码
 * @return WTSTickData* 最新的Tick数据指针,失败返回NULL
 */
WTSTickData* HisDataReplayer::get_last_tick(const char* stdCode)
{
	if (!checkTicks(stdCode, _cur_tdate))
		return NULL;

	auto& tickList = _ticks_cache[stdCode];
	if (tickList._cursor == 0)
		return NULL;

	if (tickList._cursor == UINT_MAX)
	{
		uint32_t uDate = _cur_date;
		uint32_t uTime = _cur_time * 100000 + _cur_secs;

		WTSTickStruct curTick;
		curTick.action_date = uDate;
		curTick.action_time = uTime;

		auto tit = std::lower_bound(tickList._items.begin(), tickList._items.end(), curTick, [](const WTSTickStruct& a, const WTSTickStruct& b){
			if (a.action_date != b.action_date)
				return a.action_date < b.action_date;
			else
				return a.action_time < b.action_time;
		});

		std::size_t idx = tit - tickList._items.begin();
		tickList._cursor = idx + 1;
	}
	else if (tickList._cursor > tickList._count)
		return NULL;

	return WTSTickData::create(tickList._items[tickList._cursor - 1]);
}

/*!
 * @brief 获取品种信息
 * 
 * 根据标准合约代码获取对应的品种信息
 * 包括交易所、品种属性、合约规模等信息
 * 
 * @param stdCode 标准合约代码
 * @return WTSCommodityInfo* 品种信息指针
 */
WTSCommodityInfo* HisDataReplayer::get_commodity_info(const char* stdCode)
{
	return _bd_mgr.getCommodity(CodeHelper::stdCodeToStdCommID(stdCode).c_str());
}

/*!
 * @brief 获取交易时段信息
 * 
 * 根据时段ID或合约代码获取对应的交易时段信息
 * 包括开盘时间、收盘时间、节假日安排等关键信息
 * 
 * @param sid 时段ID或合约代码
 * @param isCode 是否为合约代码，默认为false(表示时段ID)
 * @return WTSSessionInfo* 交易时段信息指针，失败返回NULL
 */
WTSSessionInfo* HisDataReplayer::get_session_info(const char* sid, bool isCode /* = false */)
{
	if (!isCode)
		return _bd_mgr.getSession(sid);

	WTSCommodityInfo* cInfo = _bd_mgr.getCommodity(CodeHelper::stdCodeToStdCommID(sid).c_str());
	if (cInfo == NULL)
		return NULL;

	return _bd_mgr.getSession(cInfo->getSession());
}

/*!
 * @brief 加载手续费模板
 * 
 * 从配置文件中加载各品种的手续费模板设置
 * 支持按手数或按金额计算手续费,包括开仓、平仓、平今手续费
 * 
 * @param filename 手续费模板文件路径
 */
void HisDataReplayer::loadFees(const char* filename)
{
	if (strlen(filename) == 0)
		return;

	if (!StdFile::exists(filename))
	{
		WTSLogger::error_f("Fees template file {} not exists", filename);
		return;
	}

	WTSVariant* cfg = WTSCfgLoader::load_from_file(filename, true);
	if (cfg == NULL)
	{
		WTSLogger::error_f("Converting fees template file {} failed", filename);
		return;
	}

	auto keys = cfg->memberNames();
	for (const std::string& key : keys)
	{
		WTSVariant* cfgItem = cfg->get(key.c_str());
		FeeItem& fItem = _fee_map[key];
		fItem._by_volume = cfgItem->getBoolean("byvolume");
		fItem._open = cfgItem->getDouble("open");
		fItem._close = cfgItem->getDouble("close");
		fItem._close_today = cfgItem->getDouble("closetoday");
	}

	cfg->release();

	WTSLogger::info_f("{} items of fees template loaded", _fee_map.size());
}


/*!
 * @brief 计算交易手续费
 * 
 * 根据预设的手续费模板计算指定合约的交易手续费
 * 支持按手数或按金额计算,包括开仓、平仓、平今手续费
 * 
 * @param stdCode 标准合约代码
 * @param price 成交价格
 * @param qty 成交数量
 * @param offset 开平标志(0-开仓, 1-平仓, 2-平今)
 * @return 计算出的手续费金额
 */
double HisDataReplayer::calc_fee(const char* stdCode, double price, double qty, uint32_t offset)
{
	std::string stdPID = CodeHelper::stdCodeToStdCommID(stdCode);
	auto it = _fee_map.find(stdPID);
	if (it == _fee_map.end())
		return 0.0;

	double ret = 0.0;
	WTSCommodityInfo* commInfo = _bd_mgr.getCommodity(stdPID.c_str());
	const FeeItem& fItem = it->second;
	if (fItem._by_volume)
	{
		switch (offset)
		{
		case 0: ret = fItem._open*qty; break;
		case 1: ret = fItem._close*qty; break;
		case 2: ret = fItem._close_today*qty; break;
		default: ret = 0.0; break;
		}
	}
	else
	{
		double amount = price*qty*commInfo->getVolScale();
		switch (offset)
		{
		case 0: ret = fItem._open*amount; break;
		case 1: ret = fItem._close*amount; break;
		case 2: ret = fItem._close_today*amount; break;
		default: ret = 0.0; break;
		}
	}

	return (int32_t)(ret * 100 + 0.5) / 100.0;
}

/*!
 * @brief 获取当前价格
 * 
 * 获取指定合约的当前价格(最新成交价)
 * 用于策略实时获取合约的最新价格信息
 * 
 * @param stdCode 标准合约代码
 * @return 当前价格,如果未找到则返回0.0
 */
double HisDataReplayer::get_cur_price(const char* stdCode)
{
	auto it = _price_map.find(stdCode);
	if (it == _price_map.end())
	{
		return 0.0;
	}
	else
	{
		return it->second;
	}
}

/*!
 * @brief 订阅Tick数据
 * 
 * 将指定的策略ID添加到指定合约的Tick数据订阅列表中
 * 用于策略请求接收某个合约的实时Tick数据推送
 * 
 * @param sid 策略ID
 * @param stdCode 标准合约代码
 */
void HisDataReplayer::sub_tick(uint32_t sid, const char* stdCode)
{
	if (strlen(stdCode) == 0)
		return;

	SIDSet& sids = _tick_sub_map[stdCode];
	sids.insert(sid);
}

/*!
 * @brief 订阅委托明细数据
 * 
 * 将指定的策略ID添加到指定合约的委托明细数据订阅列表中
 * 用于Level2数据分析和委托行为研究
 * 
 * @param sid 策略ID
 * @param stdCode 标准合约代码
 */
void HisDataReplayer::sub_order_detail(uint32_t sid, const char* stdCode)
{
	if (strlen(stdCode) == 0)
		return;

	SIDSet& sids = _orddtl_sub_map[stdCode];
	sids.insert(sid);
}

/*!
 * @brief 订阅委托队列数据
 * 
 * 将指定的策略ID添加到指定合约的委托队列数据订阅列表中
 * 用于分析买卖盘口的委托堆积情况
 * 
 * @param sid 策略ID
 * @param stdCode 标准合约代码
 */
void HisDataReplayer::sub_order_queue(uint32_t sid, const char* stdCode)
{
	if (strlen(stdCode) == 0)
		return;

	SIDSet& sids = _ordque_sub_map[stdCode];
	sids.insert(sid);
}

/*!
 * @brief 订阅成交明细数据
 * 
 * 将指定的策略ID添加到指定合约的成交明细数据订阅列表中
 * 用于分析成交行为和市场微观结构
 * 
 * @param sid 策略ID
 * @param stdCode 标准合约代码
 */
void HisDataReplayer::sub_transaction(uint32_t sid, const char* stdCode)
{
	if (strlen(stdCode) == 0)
		return;

	SIDSet& sids = _trans_sub_map[stdCode];
	sids.insert(sid);
}

/*!
 * @brief 检查未完成K线数据
 * 
 * 检查已订阅Tick数据的合约是否有对应的K线数据
 * 如果没有K线数据,则自动加载1分钟K线数据到未完成K线缓存中
 * 用于支持从K线数据模拟生成Tick数据的功能
 */
void HisDataReplayer::checkUnbars()
{
	for(auto& m : _tick_sub_map)
	{
		const char* stdCode = m.first.c_str();
		bool bHasBars = false;
		for (auto& m : _unbars_cache)
		{
			const std::string& key = m.first;
			auto ay = StrUtil::split(key, "#");
			if (ay[0].compare(stdCode) == 0)
			{
				bHasBars = true;
				break;
			}
		}
		if(bHasBars)
			continue;

		for (auto& m : _bars_cache)
		{
			const std::string& key = m.first;
			auto ay = StrUtil::split(key, "#");
			if (ay[0].compare(stdCode) == 0)
			{
				bHasBars = true;
				break;
			}
		}

		if (bHasBars)
			continue;

		//如果没有订阅tick,但是没有对应的K线数据,自动订阅1分钟线到内存中
		bool bHasHisData = false;
		std::string key = StrUtil::printf("%s#m#1", stdCode);

		/*
		 *	By Wesley @ 2021.12.20
		 *	先从extloader加载缓存的K线数据
		 *	如果不能则调用原来的历史数据存储引擎接口函数
		 */
		if (NULL != _bt_loader)
		{
			bHasHisData = cacheFinalBarsFromLoader(key, stdCode, KP_Minute1, false);
		}

		if(!bHasHisData)
		{
			if (_mode == "csv")
			{
				bHasHisData = cacheRawBarsFromCSV(key, stdCode, KP_Minute1, false);
			}
			else
			{
				bHasHisData = cacheRawBarsFromBin(key, stdCode, KP_Minute1, false);
			}
		}
		
		
		if (!bHasHisData)
			continue;

		WTSSessionInfo* sInfo = get_session_info(stdCode, true);

		BarsListPtr& kBlkPair = _unbars_cache[key];
		
		//还没有计算初始位置
		WTSBarStruct bar;
		bar.date = _cur_tdate;
		bar.time = (_cur_date - 19900000) * 10000 + _cur_time;

		auto it = std::lower_bound(kBlkPair->_bars.begin(), kBlkPair->_bars.end(), bar, [](const WTSBarStruct& a, const WTSBarStruct& b) {
			return a.time < b.time;
		});

		std::size_t eIdx = it - kBlkPair->_bars.begin();

		if (it != kBlkPair->_bars.end())
		{
			WTSBarStruct& curBar = *it;
			if (curBar.time > bar.time)
			{
				if (eIdx > 0)
				{
					it--;
					eIdx--;
				}
			}

			kBlkPair->_cursor = eIdx + 1;
		}
	}
}

/*!
 * @brief 字符串时间转换为数值时间
 * 
 * 将字符串格式的时间(如"09:30:00"或"09:30")转换为数值格式
 * 主要用于从CSV文件或配置文件中解析时间数据
 * 
 * @param strTime 字符串时间(格式如"HH:MM:SS"或"HH:MM")
 * @param bHasSec 是否包含秒数,默认为false
 * @return 数值格式的时间(HHMM或HHMMSS格式)
 */
uint32_t strToTime(const char* strTime, bool bHasSec = false)
{
	std::string str;
	const char *pos = strTime;
	while (strlen(pos) > 0)
	{
		if (pos[0] != ':')
		{
			str.append(pos, 1);
		}
		pos++;
	}

	uint32_t ret = strtoul(str.c_str(), NULL, 10);
	if (str.size() > 4 && !bHasSec)
		ret /= 100;

	return ret;
}

/*!
 * @brief 字符串日期转换为数值日期
 * 
 * 将字符串格式的日期(如"2023/12/31"或"2023-12-31")转换为数值格式
 * 支持多种分隔符格式,主要用于从CSV文件或配置文件中解析日期数据
 * 
 * @param strDate 字符串日期(格式如"YYYY/MM/DD"或"YYYY-MM-DD")
 * @return 数值格式的日期(YYYYMMDD格式)
 */
uint32_t strToDate(const char* strDate)
{
	StringVector ay = StrUtil::split(strDate, "/");
	if(ay.size() == 1)
		ay = StrUtil::split(strDate, "-");
	std::stringstream ss;
	if (ay.size() > 1)
	{
		auto pos = ay[2].find(" ");
		if (pos != std::string::npos)
			ay[2] = ay[2].substr(0, pos);
		ss << ay[0] << (ay[1].size() == 1 ? "0" : "") << ay[1] << (ay[2].size() == 1 ? "0" : "") << ay[2];
	}
	else
		ss << ay[0];

	return strtoul(ss.str().c_str(), NULL, 10);
}

/*!
 * @brief 从二进制文件缓存原始Tick数据
 * 
 * 从二进制格式的历史数据文件中加载指定合约指定日期的Tick数据到内存缓存
 * 支持主力合约、次主力合约的映射以及数据压缩格式的处理
 * 
 * @param key 缓存键值(格式: 合约代码#数据类型#周期)
 * @param stdCode 标准合约代码
 * @param uDate 日期(YYYYMMDD格式)
 * @return 是否成功加载数据
 */
bool HisDataReplayer::cacheRawTicksFromBin(const std::string& key, const char* stdCode, uint32_t uDate)
{
	CodeHelper::CodeInfo cInfo = CodeHelper::extractStdCode(stdCode);
	std::string stdPID = StrUtil::printf("%s.%s", cInfo._exchg, cInfo._product);
	
	std::string rawCode = cInfo._code;
	if (cInfo.isHot())
	{
		rawCode = _hot_mgr.getRawCode(cInfo._exchg, cInfo._product, uDate);
	}
	else if(cInfo.isSecond())
	{
		rawCode = _hot_mgr.getSecondRawCode(cInfo._exchg, cInfo._product, uDate);
	}

	std::string content;
	bool bHit = false;
	//先检查是否有HOT或SND后缀的主力合约tick文件
	if(!cInfo.isFlat())
	{
		const char* hot_flag = cInfo.isHot() ? FILE_SUF_HOT : FILE_SUF_2ND;
		//std::stringstream ss;
		//ss << _base_dir << "his/ticks/" << cInfo._exchg << "/" << uDate << "/" << cInfo._product << hot_flag << ".dsb";
		//filename = ss.str();
		//if (StdFile::exists(filename.c_str()))
		//	bHit = true;
		/*
		 *	By Wesley @ 2022.01.11
		 *	这里将直接从文件读取改为从HisDtMgr封装的接口读取
		 */
		std::string wrappCode = StrUtil::printf("%s%s", cInfo._product, hot_flag);
		bHit = _his_dt_mgr.load_raw_ticks(cInfo._exchg, wrappCode.c_str(), uDate, [&content](std::string& data) {
			content.swap(data);
		});
	}

	//如果没有找到，则获取原始合约
	if (!bHit)
	{
		//std::stringstream ss;
		//ss << _base_dir << "his/ticks/" << cInfo._exchg << "/" << uDate << "/" << rawCode << ".dsb";
		//filename = ss.str();
		/*
		 *	By Wesley @ 2022.01.11
		 *	这里将直接从文件读取改为从HisDtMgr封装的接口读取
		 */
		bHit = _his_dt_mgr.load_raw_ticks(cInfo._exchg, rawCode.c_str(), uDate, [&content](std::string& data) {
			content.swap(data);
		});
	}

	if(!bHit)
	{
		WTSLogger::warn_f("No ticks data of {} on {} found", stdCode, uDate);
		return false;
	}

	auto& ticksList = _ticks_cache[key];
	uint32_t tickcnt = 0;
	tickcnt = content.size() / sizeof(WTSTickStruct);
	ticksList._items.resize(tickcnt);
	memcpy(ticksList._items.data(), content.data(), content.size());
	
	ticksList._cursor = UINT_MAX;
	ticksList._code = stdCode;
	ticksList._date = uDate;
	ticksList._count = tickcnt;

	return true;
}

/*!
 * @brief 从数据加载器缓存原始Tick数据
 * 
 * 通过外部数据加载器接口加载指定合约指定日期的Tick数据到内存缓存
 * 这是数据源的主要加载方式,支持各种外部数据源适配
 * 
 * @param key 缓存键值(格式: 合约代码#数据类型#周期)
 * @param stdCode 标准合约代码
 * @param uDate 日期(YYYYMMDD格式)
 * @return 是否成功加载数据
 */
bool HisDataReplayer::cacheRawTicksFromLoader(const std::string& key, const char* stdCode, uint32_t uDate)
{
	if (NULL == _bt_loader)
		return false;

	auto& ticksList = _ticks_cache[key];
	ticksList._cursor = UINT_MAX;
	ticksList._code = stdCode;
	ticksList._date = uDate;
	ticksList._count = 0;

	bool bSucc = _bt_loader->loadRawHisTicks(&ticksList, stdCode, uDate, [](void* obj, WTSTickStruct* firstTick, uint32_t count) {
		HftDataList<WTSTickStruct>* ticks = (HftDataList<WTSTickStruct>*)obj;
		ticks->_items.resize(count);
		ticks->_count = count;
		memcpy(ticks->_items.data(), firstTick, sizeof(WTSTickStruct)*count);
	});

	if (!bSucc)
		return false;

	WTSLogger::info_f("{} items of back tick data of {} on {} loaded via extended loader", ticksList._count, stdCode, uDate);

	return true;
}

bool HisDataReplayer::cacheRawTicksFromCSV(const std::string& key, const char* stdCode, uint32_t uDate)
{
	if (strlen(stdCode) == 0)
		return false;

	std::stringstream ss;
	ss << _base_dir << "bin/ticks/";
	std::string path = ss.str();
	if (!StdFile::exists(path.c_str()))
		boost::filesystem::create_directories(path.c_str());
	ss << stdCode << "_tick_" << uDate << ".dsb";
	std::string filename = ss.str();
	if (StdFile::exists(filename.c_str()))
	{
		//已有格式化的历史数据文件, 则直接读取
		WTSLogger::info_f("Reading data from {}...", filename);
		std::string content;
		StdFile::read_file_content(filename.c_str(), content);
		if (content.size() < sizeof(HisTickBlockV2))
		{
			WTSLogger::error_f("Sizechecking of back tick data file {} failed", filename);
			return false;
		}

		WTSLogger::info_f("Processing file content of {}...", filename);
		proc_block_data(filename.c_str(), content, false, false);
		uint32_t tickcnt = content.size() / sizeof(WTSTickStruct);
		auto& ticksList = _ticks_cache[key];
		ticksList._items.resize(tickcnt);
		memcpy(ticksList._items.data(), content.data(), content.size());
		ticksList._cursor = UINT_MAX;
		ticksList._code = stdCode;
		ticksList._date = uDate;
		ticksList._count = tickcnt;
	}
	else
	{
		//如果没有格式化的历史数据文件, 用csv处理
		std::stringstream ss;
		ss << _base_dir << "csv/ticks/" << stdCode << "_tick_" << uDate << ".csv";
		std::string csvfile = ss.str();

		if (!StdFile::exists(csvfile.c_str()))
		{
			WTSLogger::error_f("Back tick data file {} not exists", csvfile.c_str());
			return false;
		}

		std::ifstream ifs;
		ifs.open(csvfile.c_str());

		WTSLogger::info_f("Reading data from {}...", csvfile.c_str());

		char buffer[1024];
		bool headerskipped = false;
		auto& tickList = _ticks_cache[key];
		tickList._code = stdCode;
		tickList._date = uDate;
		while (!ifs.eof())
		{
			ifs.getline(buffer, 1024);
			if (strlen(buffer) == 0)
				continue;

			//跳过头部
			if (!headerskipped)
			{
				headerskipped = true;
				continue;
			}

			//逐行读取
			StringVector ay = StrUtil::split(buffer, ",");
			WTSTickStruct ticks;
			ticks.action_date = strToDate(ay[0].c_str());
			ticks.action_time = strToTime(ay[1].c_str(), true) * 1000;
			ticks.price = strtod(ay[2].c_str(), NULL);
			ticks.volume = strtod(ay[3].c_str(), NULL);
			tickList._items.emplace_back(ticks);

			if (tickList._items.size() % 1000 == 0)
			{
				WTSLogger::info_f("{} items of data loaded", tickList._items.size());
			}
		}
		tickList._count = tickList._items.size();
		ifs.close();
		WTSLogger::info_f("Data file {} all loaded, totally {} items", csvfile.c_str(), tickList._items.size());

		/*
		 *	By Wesley @ 2021.12.14
		 *	修复之前的bug，之前没有把文件头写入文件，在转换到dsb格式时会抛出异常
		 */
		std::string content;
		content.resize(sizeof(HisTickBlockV2));
		HisTickBlockV2 *pBlk = (HisTickBlockV2*)content.data();
		strcpy(pBlk->_blk_flag, BLK_FLAG);
		pBlk->_type = BT_HIS_Ticks;
		pBlk->_version = BLOCK_VERSION_CMP_V2;

		std::string cmpData = WTSCmpHelper::compress_data(tickList._items.data(), sizeof(WTSTickStruct)*tickList._count);
		pBlk->_size = cmpData.size();
		content.append(cmpData);

		StdFile::write_file_content(filename.c_str(), content.c_str(), content.size());
		WTSLogger::info_f("Ticks transfered to file {}", filename.c_str());
	}

	return true;
}

/*!
 * @brief 从数据加载器缓存最终K线数据
 * 
 * 通过外部数据加载器接口加载指定合约的最终K线数据到内存缓存
 * 优先从已转换的dsb二进制文件加载,如果没有则通过加载器接口获取
 * 支持自动转换和保存为高效的二进制格式以提升后续加载性能
 * 
 * @param key 缓存键值(格式: 合约代码#数据类型#周期)
 * @param stdCode 标准合约代码
 * @param period K线周期(分钟线、5分钟线、日线等)
 * @param bSubbed 是否为订阅数据,默认为true
 * @return 是否成功加载数据
 */
bool HisDataReplayer::cacheFinalBarsFromLoader(const std::string& key, const char* stdCode, WTSKlinePeriod period, bool bSubbed /* = true */)
{
	if (NULL == _bt_loader)
		return false;

	CodeHelper::CodeInfo cInfo = CodeHelper::extractStdCode(stdCode);
	WTSCommodityInfo* commInfo = _bd_mgr.getCommodity(cInfo._exchg, cInfo._product);
	std::string stdPID = StrUtil::printf("%s.%s", cInfo._exchg, cInfo._product);

	std::string pname;
	std::string dirname;
	switch (period)
	{
	case KP_Minute1: pname = "m1"; dirname = "min1"; break;
	case KP_Minute5: pname = "m5"; dirname = "min5"; break;
	case KP_DAY: pname = "d"; dirname = "day"; break;
	default: pname = ""; break;
	}

	bool isDay = (period == KP_DAY);

	std::stringstream ss;
	ss << _base_dir << "his/" << dirname << "/" << cInfo._exchg << "/";
	if (!StdFile::exists(ss.str().c_str()))
        boost::filesystem::create_directories(ss.str().c_str());

	if (cInfo.isHot() && commInfo->isFuture())
	{
		ss << cInfo._exchg << "." << cInfo._product << "_HOT.dsb";
	}
	else if (cInfo.isSecond() && commInfo->isFuture())
	{
		ss << cInfo._exchg << "." << cInfo._product << "_2ND.dsb";
	}
	else if (cInfo.isExright() && commInfo->isStock())
	{

	}
	else
		ss << cInfo._code << ".dsb";
	std::string filename = ss.str();

	bool bHit = false;
	if(_bt_loader->isAutoTrans() && StdFile::exists(filename.c_str()))
	{
		//如果支持自动转换，先读取已经转换的dsb文件
		std::string content;
		StdFile::read_file_content(filename.c_str(), content);
		if (content.size() < sizeof(HisKlineBlockV2))
		{
			WTSLogger::error_f("Sizechecking of back kbar data file {} failed", filename.c_str());
		}
		else
		{
			HisKlineBlockV2* kBlock = (HisKlineBlockV2*)content.c_str();
			std::string rawData = WTSCmpHelper::uncompress_data(kBlock->_data, (uint32_t)kBlock->_size);
			uint32_t barcnt = rawData.size() / sizeof(WTSBarStruct);

			if (bSubbed)
				_bars_cache[key].reset(new BarsList);
			else
				_unbars_cache[key].reset(new BarsList);

			BarsListPtr& barsList = bSubbed ? _bars_cache[key] : _unbars_cache[key];
			barsList->_bars.resize(barcnt);
			memcpy(barsList->_bars.data(), rawData.data(), rawData.size());
			barsList->_cursor = UINT_MAX;
			barsList->_code = stdCode;
			barsList->_period = period;
			barsList->_count = barcnt;

			uint64_t stime = isDay ? barsList->_bars[0].date : barsList->_bars[0].time;
			uint64_t etime = isDay ? barsList->_bars[barcnt - 1].date : barsList->_bars[barcnt - 1].time;

			WTSLogger::info_f("{} items of back {} data of {} directly loaded from dsb file, from {} to {}", barcnt, pname.c_str(), stdCode, stime, etime);
			bHit = true;
		}
	}

	if(!bHit)
	{
		//如果没有转换的历史数据文件, 用csv处理
		WTSLogger::log_raw(LL_INFO, "Reading data via extended loader...");

		if (bSubbed)
			_bars_cache[key].reset(new BarsList);
		else
			_unbars_cache[key].reset(new BarsList);

		BarsListPtr& barsList = bSubbed ? _bars_cache[key] : _unbars_cache[key];
		barsList->_code = stdCode;
		barsList->_period = period;
		barsList->_cursor = UINT_MAX;
		barsList->_count = 0;

		std::string buffer;
		bool bSucc = _bt_loader->loadFinalHisBars(barsList.get(), stdCode, period, [](void* obj, WTSBarStruct* firstBar, uint32_t count) {
			BarsList* bars = (BarsList*)obj;
			bars->_count = count;
			bars->_bars.resize(count);
			memcpy((void*)bars->_bars.data(), firstBar, sizeof(WTSBarStruct)*count);
		});

		if (!bSucc)
			return false;

		bool isDay = (period == KP_DAY);

		uint64_t stime = isDay ? barsList->_bars[0].date : barsList->_bars[0].time;
		uint64_t etime = isDay ? barsList->_bars[barsList->_count - 1].date : barsList->_bars[barsList->_count - 1].time;

		WTSLogger::info_f("{} items of back {} data of {} loaded via extended loader, from {} to {}", barsList->_count, pname.c_str(), stdCode, stime, etime);

		if(_bt_loader->isAutoTrans())
		{
			BlockType btype;
			switch (period)
			{
			case KP_Minute1: btype = BT_HIS_Minute1; break;
			case KP_Minute5: btype = BT_HIS_Minute5; break;
			default: btype = BT_HIS_Day; break;
			}

			/*
			 *	By Wesley @ 2021.12.14
			 *	修复之前的bug，之前没有把文件头写入文件，在转换到dsb格式时会抛出异常
			 */
			std::string content;
			content.resize(sizeof(HisKlineBlockV2));
			HisKlineBlockV2 *kBlock = (HisKlineBlockV2*)content.data();
			strcpy(kBlock->_blk_flag, BLK_FLAG);
			kBlock->_type = btype;
			kBlock->_version = BLOCK_VERSION_CMP_V2;

			std::string cmpData = WTSCmpHelper::compress_data(barsList->_bars.data(), sizeof(WTSBarStruct)*barsList->_count);
			kBlock->_size = cmpData.size();
			content.append(cmpData);

			StdFile::write_file_content(filename.c_str(), content.c_str(), content.size());
			WTSLogger::info_f("Bars transfered to file {}", filename);
		}
	}

	return true;
}

/*!
 * @brief 从CSV文件缓存原始K线数据
 * 
 * 从CSV格式的历史数据文件中加载指定合约的K线数据到内存缓存
 * 支持多种周期的K线数据,并自动转换为内部数据格式
 * 可将数据保存为二进制dsb格式以提高后续加载速度
 * 
 * @param key 缓存键值(格式: 合约代码#数据类型#周期)
 * @param stdCode 标准合约代码
 * @param period K线周期(分钟线、5分钟线、日线等)
 * @param bSubbed 是否为订阅数据,默认为true
 * @return 是否成功加载数据
 */
bool HisDataReplayer::cacheRawBarsFromCSV(const std::string& key, const char* stdCode, WTSKlinePeriod period, bool bSubbed/* = true*/)
{
	CodeHelper::CodeInfo cInfo = CodeHelper::extractStdCode(stdCode);
	WTSCommodityInfo* commInfo = _bd_mgr.getCommodity(cInfo._exchg, cInfo._product);
	std::string stdPID = StrUtil::printf("%s.%s", cInfo._exchg, cInfo._product);

	std::string p_suffix;
	std::string dirname = PERIOD_NAME[period];
	switch (period)
	{
	case KP_Minute1: p_suffix = "m1"; break;
	case KP_Minute5: p_suffix = "m5"; break;
	case KP_DAY: p_suffix = "d"; break;
	default: p_suffix = ""; break;
	}

	bool isDay = (period == KP_DAY);

	std::stringstream ss;
	ss << _base_dir << "his/" << dirname << "/" << cInfo._exchg << "/";

	//如果是自动转换，则不生成转换是必要
	if (!StdFile::exists(ss.str().c_str()))
		boost::filesystem::create_directories(ss.str().c_str());

	if(cInfo.isHot() && commInfo->isFuture())
	{
		ss << cInfo._exchg << "." << cInfo._product << "_HOT.dsb";
	}
	else if (cInfo.isSecond() && commInfo->isFuture())
	{
		ss << cInfo._exchg << "." << cInfo._product << "_2ND.dsb";
	}
	else if (cInfo.isExright() && commInfo->isStock())
	{

	}
	else
		ss << cInfo._code << ".dsb";
	std::string filename = ss.str();
	if (StdFile::exists(filename.c_str()))
	{
		//如果有格式化的历史数据文件, 就直接读取
		std::string content;
		StdFile::read_file_content(filename.c_str(), content);
		if (content.size() < sizeof(HisKlineBlockV2))
		{
			WTSLogger::error_f("Sizechecking of back kbar data file {} failed", filename);
			return false;
		}

		//By Wesley @ 2021.12.30
		//转换完数据不用缓存，直接存入生成即可
		proc_block_data(filename.c_str(), content, true, false);
		uint32_t barcnt = content.size() / sizeof(WTSBarStruct);

		if (bSubbed)
			_bars_cache[key].reset(new BarsList);
		else
			_unbars_cache[key].reset(new BarsList);

		BarsListPtr& barsList = bSubbed ? _bars_cache[key] : _unbars_cache[key];
		barsList->_bars.resize(barcnt);
		memcpy(barsList->_bars.data(), content.data(), content.size());
		barsList->_cursor = UINT_MAX;
		barsList->_code = stdCode;
		barsList->_period = period;
		barsList->_count = barcnt;

		uint64_t stime = isDay ? barsList->_bars[0].date : barsList->_bars[0].time;
		uint64_t etime = isDay ? barsList->_bars[barcnt-1].date : barsList->_bars[barcnt-1].time;

		WTSLogger::info_f("{} items of back {} data of {} directly loaded from dsb file, from {} to {}", barcnt, p_suffix.c_str(), stdCode, stime, etime);
	}
	else
	{
		//如果没有格式化的历史数据文件, 用csv处理
		std::stringstream ss;
		ss << _base_dir << "csv/" << stdCode << "_" << p_suffix << ".csv";
		std::string csvfile = ss.str();

		if (!StdFile::exists(csvfile.c_str()))
		{
			WTSLogger::error_f("Back kbar data file {} not exists", csvfile);
			return false;
		}

		CsvReader reader;
		reader.load_from_file(csvfile.c_str());

		WTSLogger::info_f("Reading data from {}, with fields: {}...", csvfile, reader.fields());

		if (bSubbed)
			_bars_cache[key].reset(new BarsList);
		else
			_unbars_cache[key].reset(new BarsList);

		BarsListPtr& barsList = bSubbed ? _bars_cache[key] : _unbars_cache[key];
		barsList->_code = stdCode;
		barsList->_period = period;
		while (reader.next_row())
		{
			//逐行读取
			WTSBarStruct bs;
			bs.date = strToDate(reader.get_string("date"));
			if (period != KP_DAY)
				bs.time = TimeUtils::timeToMinBar(bs.date, strToTime(reader.get_string("time")));
			bs.open = reader.get_double("open");
			bs.high = reader.get_double("high");
			bs.low = reader.get_double("low");
			bs.close = reader.get_double("close");
			bs.vol = reader.get_uint32("volume");
			bs.money = reader.get_double("turnover");
			bs.hold = reader.get_uint32("open_interest");
			bs.add = reader.get_int32("diff_interest");
			bs.settle = reader.get_double("settle");
			barsList->_bars.emplace_back(bs);

			if (barsList->_bars.size() % 1000 == 0)
			{
				WTSLogger::info_f("{} lines of data loaded", barsList->_bars.size());
			}
		}
		barsList->_count = barsList->_bars.size();

		uint64_t stime = isDay ? barsList->_bars[0].date : barsList->_bars[0].time;
		uint64_t etime = isDay ? barsList->_bars[barsList->_count - 1].date : barsList->_bars[barsList->_count - 1].time;

		WTSLogger::info_f("Data file {} all loaded, totally {} items, from {} to {}", csvfile.c_str(), barsList->_bars.size(), stime, etime);

		BlockType btype;
		switch (period)
		{
		case KP_Minute1: btype = BT_HIS_Minute1; break;
		case KP_Minute5: btype = BT_HIS_Minute5; break;
		default: btype = BT_HIS_Day; break;
		}

		/*
		 *	By Wesley @ 2021.12.14
		 *	修复之前的bug，之前没有把文件头写入文件，在转换到dsb格式时会抛出异常
		 */
		std::string content;
		content.resize(sizeof(HisKlineBlockV2));
		HisKlineBlockV2 *kBlock = (HisKlineBlockV2*)content.data();
		strcpy(kBlock->_blk_flag, BLK_FLAG);
		kBlock->_type = btype;
		kBlock->_version = BLOCK_VERSION_CMP_V2;

		std::string cmpData = WTSCmpHelper::compress_data(barsList->_bars.data(), sizeof(WTSBarStruct)*barsList->_count);
		kBlock->_size = cmpData.size();
		content.append(cmpData);

		StdFile::write_file_content(filename.c_str(), content.c_str(), content.size());
		WTSLogger::info_f("Bars transfered to file {}", filename);
	}

	return true;
}

/*!
 * @brief 从二进制文件缓存整合的期货K线数据
 * 
 * 专门处理期货主力合约和次主力合约的K线数据整合
 * 将多个子合约的历史数据按照换月规则拼接成连续的主力合约数据
 * 涉及复杂的换月逻辑、数据边界处理和时间戳转换
 * 
 * @param key 缓存键值(格式: 合约代码#数据类型#周期)
 * @param stdCode 标准合约代码(HOT或2ND后缀)
 * @param period K线周期(分钟线、5分钟线、日线等)
 * @param bSubbed 是否为订阅数据,默认为true
 * @return 是否成功加载和整合数据
 */
bool HisDataReplayer::cacheIntegratedFutBarsFromBin(const std::string& key, const char* stdCode, WTSKlinePeriod period, bool bSubbed /* = true */)
{
	CodeHelper::CodeInfo cInfo = CodeHelper::extractStdCode(stdCode);
	std::string stdPID = StrUtil::printf("%s.%s", cInfo._exchg, cInfo._product);

	uint32_t curDate = TimeUtils::getCurDate();
	uint32_t curTime = TimeUtils::getCurMin() / 100;

	uint32_t endTDate = _bd_mgr.calcTradingDate(stdPID.c_str(), curDate, curTime, false);

	std::string pname;
	switch (period)
	{
	case KP_Minute1: pname = "min1"; break;
	case KP_Minute5: pname = "min5"; break;
	default: pname = "day"; break;
	}

	_bars_cache[key].reset(new BarsList());
	BarsListPtr& barsList = _bars_cache[key];
	barsList->_code = stdCode;
	barsList->_period = period;

	std::vector<std::vector<WTSBarStruct>*> barsSections;

	uint32_t realCnt = 0;
	bool isDay = (period == KP_DAY);

	const char* hot_flag = cInfo.isHot() ? FILE_SUF_HOT : FILE_SUF_2ND;

	std::vector<WTSBarStruct>* hotAy = NULL;
	uint64_t lastHotTime = 0;
	do
	{
		/*
		 *	By Wesley @ 2021.12.20
		 *	这里我们需要先调用_loader->loadRawHisBars从外部加载器获取合约数据的
		 *	上面不再调用loadFinalHisBars了，再调用loadRawHisBars就可以了，所以直接跳过
		 *
		 *	@ 2022.01.11
		 *	这里将文件读取改为从HisDtMgr封装的接口读取
		 */
		std::string content;
		std::string wrappCode = StrUtil::printf("%s.%s%s", cInfo._exchg, cInfo._product, hot_flag);
		bool bSucc = _his_dt_mgr.load_raw_bars(cInfo._exchg, wrappCode.c_str(), period, [&content](std::string& data) {
			content.swap(data);
		});

		if(!bSucc)
		{
			WTSLogger::error_f("Loading {} bars of {} via HisDtMgr failed", PERIOD_NAME[period], stdCode);
			break;
		}

		uint32_t barcnt = content.size() / sizeof(WTSBarStruct);
		if (barcnt <= 0)
			break;

		hotAy = new std::vector<WTSBarStruct>();
		hotAy->resize(barcnt);
		memcpy(hotAy->data(), content.data(), content.size());

		if (period != KP_DAY)
			lastHotTime = hotAy->at(barcnt - 1).time;
		else
			lastHotTime = hotAy->at(barcnt - 1).date;

		uint64_t stime = isDay ? barsList->_bars[0].date : barsList->_bars[0].time;
		uint64_t etime = isDay ? barsList->_bars[barcnt - 1].date : barsList->_bars[barcnt - 1].time;

		WTSLogger::info_f("{} items of back {} data of hot contract {} directly loaded, from {} to {}", barcnt, pname.c_str(), stdCode, stime, etime);

	} while (false);

	HotSections secs;
	if (cInfo.isHot())
	{
		if (!_hot_mgr.splitHotSecions(cInfo._exchg, cInfo._product, 19900102, endTDate, secs))
			return false;
	}
	else if (cInfo.isSecond())
	{
		if (!_hot_mgr.splitSecondSecions(cInfo._exchg, cInfo._product, 19900102, endTDate, secs))
			return false;
	}

	if (secs.empty())
		return false;

	bool bAllCovered = false;
	for (auto it = secs.rbegin(); it != secs.rend(); it++)
	{
		const HotSection& hotSec = *it;
		const char* curCode = hotSec._code.c_str();
		uint32_t rightDt = hotSec._e_date;
		uint32_t leftDt = hotSec._s_date;

		//要先将日期转换为边界时间
		WTSBarStruct sBar, eBar;
		if (period != KP_DAY)
		{
			uint64_t sTime = _bd_mgr.getBoundaryTime(stdPID.c_str(), leftDt, false, true);
			uint64_t eTime = _bd_mgr.getBoundaryTime(stdPID.c_str(), rightDt, false, false);

			sBar.date = leftDt;
			sBar.time = ((uint32_t)(sTime / 10000) - 19900000) * 10000 + (uint32_t)(sTime % 10000);

			if (sBar.time < lastHotTime)	//左边界时间小于上个主力最后一个Bar的时间, 说明已经有交集了, 不需要再处理了
			{
				bAllCovered = true;
				sBar.time = lastHotTime + 1;
			}

			eBar.date = rightDt;
			eBar.time = ((uint32_t)(eTime / 10000) - 19900000) * 10000 + (uint32_t)(eTime % 10000);

			if (eBar.time <= lastHotTime)	//右边界时间小于上一个Hot时间, 说明全部都重复了, 没有处理的必要了
				break;
		}
		else
		{
			sBar.date = leftDt;
			if (sBar.date < lastHotTime)	//左边界时间小于上个主力最后一个Bar的时间, 说明已经有交集了, 不需要再处理了
			{
				bAllCovered = true;
				sBar.date = (uint32_t)lastHotTime + 1;
			}

			eBar.date = rightDt;

			if (eBar.date <= lastHotTime)
				break;
		}

		/*
		 *	By Wesley @ 2021.12.20
		 *	先从extloader获取未复权K线数据
		 *	如果没有再从文件读取
		 */
		bool bLoaded = false;
		std::string buffer;
		if (NULL != _bt_loader)
		{
			//加载合约数据
			std::string wCode = StrUtil::printf("%s.%s.%s", cInfo._exchg, cInfo._product, (char*)curCode + strlen(cInfo._product));
			bLoaded = _bt_loader->loadRawHisBars(&buffer, wCode.c_str(), period, [](void* obj, WTSBarStruct* bars, uint32_t count) {
				std::string* buff = (std::string*)obj;
				buff->resize(sizeof(WTSBarStruct)*count);
				memcpy((void*)buff->c_str(), bars, sizeof(WTSBarStruct)*count);
			});

		}

		if (!bLoaded)
		{
			bLoaded = _his_dt_mgr.load_raw_bars(cInfo._exchg, curCode, period, [&buffer](std::string& data) {
				buffer.swap(data);
			});

			if (!bLoaded)
			{
				WTSLogger::error_f("Loading {} bars of {} via HisDtMgr failed", PERIOD_NAME[period], curCode);
				break;
			}
		}

		if (buffer.empty())
			break;

		uint32_t barcnt = buffer.size() / sizeof(WTSBarStruct);

		WTSBarStruct* firstBar = (WTSBarStruct*)buffer.data();

		WTSBarStruct* pBar = std::lower_bound(firstBar, firstBar + (barcnt - 1), sBar, [period](const WTSBarStruct& a, const WTSBarStruct& b) {
			if (period == KP_DAY)
			{
				return a.date < b.date;
			}
			else
			{
				return a.time < b.time;
			}
		});

		std::size_t sIdx = pBar - firstBar;
		if ((period == KP_DAY && pBar->date < sBar.date) || (period != KP_DAY && pBar->time < sBar.time))	//小于边界时间
		{
			//小于边界时间, 说明没有数据, 因为lower_bound会返回大于等于目标位置的迭代器
			continue;
		}

		pBar = std::lower_bound(firstBar + sIdx, firstBar + (barcnt - 1), eBar, [period](const WTSBarStruct& a, const WTSBarStruct& b) {
			if (period == KP_DAY)
			{
				return a.date < b.date;
			}
			else
			{
				return a.time < b.time;
			}
		});
		std::size_t eIdx = pBar - firstBar;
		if ((period == KP_DAY && pBar->date > eBar.date) || (period != KP_DAY && pBar->time > eBar.time))
		{
			pBar--;
			eIdx--;
		}

		if (eIdx < sIdx)
			continue;

		uint32_t curCnt = eIdx - sIdx + 1;
		std::vector<WTSBarStruct>* tempAy = new std::vector<WTSBarStruct>();
		tempAy->resize(curCnt);
		memcpy(tempAy->data(), &firstBar[sIdx], sizeof(WTSBarStruct)*curCnt);
		realCnt += curCnt;

		barsSections.emplace_back(tempAy);

		if (bAllCovered)
			break;
	}

	if (hotAy)
	{
		barsSections.emplace_back(hotAy);
		realCnt += hotAy->size();
	}

	if (realCnt > 0)
	{
		barsList->_bars.resize(realCnt);

		uint32_t curIdx = 0;
		for (auto it = barsSections.rbegin(); it != barsSections.rend(); it++)
		{
			std::vector<WTSBarStruct>* tempAy = *it;
			memcpy(barsList->_bars.data() + curIdx, tempAy->data(), tempAy->size() * sizeof(WTSBarStruct));
			curIdx += tempAy->size();
			delete tempAy;
		}
		barsSections.clear();
	}

	WTSLogger::info_f("{} items of back {} data of {} cached", realCnt, pname, stdCode);

	return true;
}

/*!
 * @brief 获取股票复权因子列表
 * 
 * 获取指定股票代码的复权因子数据,用于计算前复权或后复权价格
 * 如果缓存中没有数据,会尝试通过外部数据加载器获取
 * 复权因子按日期排序,确保复权计算的准确性
 * 
 * @param code 股票代码
 * @param exchg 交易所代码
 * @param pid 产品代码,默认为空
 * @return 复权因子列表的常量引用
 */
const HisDataReplayer::AdjFactorList& HisDataReplayer::getAdjFactors(const char* code, const char* exchg, const char* pid /* = "" */)
{
	char key[20] = { 0 };
	sprintf(key, "%s.%s.%s", exchg, pid, code);

	auto it = _adj_factors.find(key);
	if (it == _adj_factors.end())
	{
		//By Wesley @ 2021.12.21
		//如果没有复权因子，就从extloader加载一下
		if (_bt_loader)
		{
            WTSLogger::info_f("No adjusting factors of {} cached, searching via extented loader...", key);
			_bt_loader->loadAdjFactors(this, key, [](void* obj, const char* stdCode, uint32_t* dates, double* factors, uint32_t count) {
				HisDataReplayer* self = (HisDataReplayer*)obj;
				AdjFactorList& fctrLst = self->_adj_factors[stdCode];

				for (uint32_t i = 0; i < count; i++)
				{
					AdjFactor adjFact;
					adjFact._date = dates[i];
					adjFact._factor = factors[i];

					fctrLst.emplace_back(adjFact);
				}

				//一定要把第一个加进去，不然如果是前复权的话，可能会漏掉最早的数据
				AdjFactor adjFact;
				adjFact._date = 19900101;
				adjFact._factor = 1;
				fctrLst.emplace_back(adjFact);

				std::sort(fctrLst.begin(), fctrLst.end(), [](const AdjFactor& left, const AdjFactor& right) {
					return left._date < right._date;
				});

                WTSLogger::info_f("{} items of adjusting factors of {} loaded via extended loader", count, stdCode);
			});
		}
	}

	return _adj_factors[key];
}

/*!
 * @brief 从二进制文件缓存股票复权K线数据
 * 
 * 处理股票的前复权或后复权K线数据加载和计算
 * 首先加载已复权的历史数据,然后加载原始数据进行复权计算
 * 涉及复权因子的加载、应用和价格调整,确保连续性
 * 
 * @param key 缓存键值(格式: 合约代码#数据类型#周期)
 * @param stdCode 标准合约代码(含复权标志)
 * @param period K线周期(分钟线、5分钟线、日线等)
 * @param bSubbed 是否为订阅数据,默认为true
 * @return 是否成功加载和处理复权数据
 */
bool HisDataReplayer::cacheAdjustedStkBarsFromBin(const std::string& key, const char* stdCode, WTSKlinePeriod period, bool bSubbed /* = true */)
{
	CodeHelper::CodeInfo cInfo = CodeHelper::extractStdCode(stdCode);
	std::string stdPID = StrUtil::printf("%s.%s", cInfo._exchg, cInfo._product);

	uint32_t curDate = TimeUtils::getCurDate();
	uint32_t curTime = TimeUtils::getCurMin() / 100;

	uint32_t endTDate = _bd_mgr.calcTradingDate(stdPID.c_str(), curDate, curTime, false);

	_bars_cache[key].reset(new BarsList());
	BarsListPtr& barsList = _bars_cache[key];
	barsList->_code = stdCode;
	barsList->_period = period;

	std::vector<std::vector<WTSBarStruct>*> barsSections;

	uint32_t realCnt = 0;

	std::vector<WTSBarStruct>* adjustedBars = NULL;
	uint64_t lastQTime = 0;

	WTSLogger::info_f("Loading adjusted bars of {}...", stdCode);
	do
	{
		//直接读取复权后的历史数据,路径如/his/day/sse/SH600000Q.dsb

		/*
		 *	By Wesley @ 2021.12.20
		 *	这里我们需要先调用_loader->loadRawHisBars从外部加载器获取合约数据的
		 *	上面不再调用loadFinalHisBars了，再调用loadRawHisBars就可以了，所以直接跳过
		 *	
		 *	@ 2022.01.11
		 *	这里将文件读取改为从HisDtMgr封装的接口读取
		 */
		std::string wrappCode = fmt::format("{}{}", cInfo._code, (cInfo._exright == 1 ? SUFFIX_QFQ : SUFFIX_HFQ));
		std::string content;
		bool bSucc = _his_dt_mgr.load_raw_bars(cInfo._exchg, wrappCode.c_str(), period, [&content](std::string& data) {
			content.swap(data);
		});

		if(!bSucc)
		{
			WTSLogger::error_f("Loading {} bars of {} via HisDtMgr failed", PERIOD_NAME[period], stdCode);
			break;
		}

		uint32_t barcnt = content.size() / sizeof(WTSBarStruct);
		if (barcnt <= 0)
			break;

		adjustedBars = new std::vector<WTSBarStruct>();
		adjustedBars->resize(barcnt);
		memcpy(adjustedBars->data(), content.data(), content.size());

		if (period != KP_DAY)
			lastQTime = adjustedBars->at(barcnt - 1).time;
		else
			lastQTime = adjustedBars->at(barcnt - 1).date;

		WTSLogger::info_f("{} items of adjusted back {} data of {} directly loaded", barcnt, PERIOD_NAME[period], stdCode);
	} while (false);

	WTSLogger::info_f("Loading raw bars of {}...", stdCode);
	do
	{
		const char* curCode = cInfo._code;

		//要先将日期转换为边界时间
		WTSBarStruct sBar;
		if (period != KP_DAY)
		{
			sBar.date = TimeUtils::minBarToDate(lastQTime);

			sBar.time = lastQTime + 1;
		}
		else
		{
			sBar.date = (uint32_t)lastQTime + 1;
		}

		/*
		 *	By Wesley @ 2021.12.20
		 *	先从extloader获取未复权K线数据
		 *	如果没有再从文件读取
		 */
		bool bLoaded = false;
		std::string buffer;
		if (NULL != _bt_loader)
		{
			std::string wCode = StrUtil::printf("%s.%s.%s", cInfo._exchg, cInfo._product, curCode);
			bLoaded = _bt_loader->loadRawHisBars(&buffer, wCode.c_str(), period, [](void* obj, WTSBarStruct* bars, uint32_t count) {
				std::string* buff = (std::string*)obj;
				buff->resize(sizeof(WTSBarStruct)*count);
				memcpy((void*)buff->c_str(), bars, sizeof(WTSBarStruct)*count);
			});

			if (bLoaded)
				WTSLogger::debug_f("Raw bars of {} loaded via extended loader", stdCode);
		}

		if (!bLoaded)
		{
			/*
			 *	By Wesley @ 2022.01.11
			 *	这里将文件读取改为从HisDtMgr封装的接口读取
			 */
			bLoaded = _his_dt_mgr.load_raw_bars(cInfo._exchg, curCode, period, [&buffer](std::string& data) {
				buffer.swap(data);
			});

			if (!bLoaded)
			{
				WTSLogger::warn_f("Loading {} bars of {} via HisDtMgr failed", PERIOD_NAME[period], curCode);
				continue;
			}
		}

		if (buffer.empty())
			break;
		
		std::size_t barcnt = buffer.size() / sizeof(WTSBarStruct);

		WTSBarStruct* firstBar = (WTSBarStruct*)buffer.data();

		WTSBarStruct* pBar = std::lower_bound(firstBar, firstBar + (barcnt - 1), sBar, [period](const WTSBarStruct& a, const WTSBarStruct& b) {
			if (period == KP_DAY)
			{
				return a.date < b.date;
			}
			else
			{
				return a.time < b.time;
			}
		});

		if (pBar != NULL)
		{
			std::size_t sIdx = pBar - firstBar;
			std::size_t curCnt = barcnt - sIdx;
			std::vector<WTSBarStruct>* tempAy = new std::vector<WTSBarStruct>();
			tempAy->resize(curCnt);
			memcpy(tempAy->data(), &firstBar[sIdx], sizeof(WTSBarStruct)*curCnt);
			realCnt += curCnt;

			auto& ayFactors = getAdjFactors(cInfo._code, cInfo._exchg, cInfo._product);
			if (!ayFactors.empty())
			{
				WTSLogger::info_f("Adjusting bars of {} with adjusting factors...", stdCode);
				//复权处理
				std::size_t lastIdx = curCnt;
				WTSBarStruct bar;
				firstBar = tempAy->data();

				//根据复权因子计算复权价格
				//如果是前复权的话，最后一个复权因子为1，如果是后复权的话，第一个复权因子为1
				//所以前复权的时候，我们要从最后一个复权因子开始，后复权的时候，我们从第一个复权因子开始
				double baseFactor = 1.0;
				if (cInfo._exright == 1)
					baseFactor = ayFactors.back()._factor;
				else if (cInfo._exright == 2)
					barsList->_factor = ayFactors.back()._factor;

				for (auto it = ayFactors.rbegin(); it != ayFactors.rend(); it++)
				{
					const AdjFactor& adjFact = *it;
					bar.date = adjFact._date;

					//计算复权因子
					double factor = adjFact._factor / baseFactor;

					WTSBarStruct* pBar = NULL;
					pBar = std::lower_bound(firstBar, firstBar + lastIdx - 1, bar, [period](const WTSBarStruct& a, const WTSBarStruct& b) {
						return a.date < b.date;
					});

					if (pBar->date < bar.date)
						continue;

					WTSBarStruct* endBar = pBar;
					if (pBar != NULL)
					{
						std::size_t curIdx = pBar - firstBar;
						while (pBar && curIdx < lastIdx)
						{
							pBar->open *= factor;
							pBar->high *= factor;
							pBar->low *= factor;
							pBar->close *= factor;

							pBar++;
							curIdx++;
						}
						lastIdx = endBar - firstBar;
					}

					if (lastIdx == 0)
						break;
				}
			}
			else
			{
				WTSLogger::info_f("No adjusting factors of {} found, ajusting task skipped...", stdCode);
			}

			barsSections.emplace_back(tempAy);
		}
	} while (false);

	if (adjustedBars)
	{
		barsSections.emplace_back(adjustedBars);
		realCnt += adjustedBars->size();
	}

	if (realCnt > 0)
	{
		barsList->_bars.resize(realCnt);

		uint32_t curIdx = 0;
		for (auto it = barsSections.rbegin(); it != barsSections.rend(); it++)
		{
			std::vector<WTSBarStruct>* tempAy = *it;
			memcpy(barsList->_bars.data() + curIdx, tempAy->data(), tempAy->size() * sizeof(WTSBarStruct));
			curIdx += tempAy->size();
			delete tempAy;
		}
		barsSections.clear();
	}

	WTSLogger::info_f("{} items of back {} data of {} cached", realCnt, PERIOD_NAME[period], stdCode);

	return true;
}


/*!
 * @brief 从二进制文件缓存原始K线数据
 * 
 * 从二进制dsb格式文件中加载指定合约的原始K线数据到内存缓存
 * 支持数据压缩格式的自动解压和多种周期的K线数据
 * 这是最基础的K线数据加载函数,为其他高级功能提供数据支撑
 * 
 * @param key 缓存键值(格式: 合约代码#数据类型#周期)
 * @param stdCode 标准合约代码
 * @param period K线周期(分钟线、5分钟线、日线等)
 * @param bSubbed 是否为订阅数据,默认为true
 * @return 是否成功加载数据
 */
bool HisDataReplayer::cacheRawBarsFromBin(const std::string& key, const char* stdCode, WTSKlinePeriod period, bool bSubbed/* = true*/)
{
	CodeHelper::CodeInfo cInfo = CodeHelper::extractStdCode(stdCode);
	WTSCommodityInfo* commInfo = _bd_mgr.getCommodity(cInfo._exchg, cInfo._product);
	std::string stdPID = StrUtil::printf("%s.%s", cInfo._exchg, cInfo._product);

	uint32_t curDate = TimeUtils::getCurDate();
	uint32_t curTime = TimeUtils::getCurMin() / 100;

	uint32_t endTDate = _bd_mgr.calcTradingDate(stdPID.c_str(), curDate, curTime, false);

	bool isDay = (period == KP_DAY);

	if (bSubbed)
		_bars_cache[key].reset(new BarsList);
	else
		_unbars_cache[key].reset(new BarsList);

	BarsListPtr& barsList = bSubbed ? _bars_cache[key] : _unbars_cache[key];
	barsList->_code = stdCode;
	barsList->_period = period;

	std::vector<std::vector<WTSBarStruct>*> barsSections;

	uint32_t realCnt = 0;
	if (!cInfo.isFlat() && commInfo->isFuture())//如果是期货主力合约，直接从整合的文件获取
	{
		return cacheIntegratedFutBarsFromBin(key, stdCode, period);
	}
	else if (cInfo.isExright() && commInfo->isStock())//如果是股票复权数据，直接从复权的文件获取
	{
		return cacheAdjustedStkBarsFromBin(key, stdCode, period, bSubbed);
	}
	

	/*
	 *	By Wesley @ 2021.12.20
	 *	先从extloader获取
	 *	如果没有再从文件获取
	 */
	bool bLoaded = false;
	std::string buffer;
	if (NULL != _bt_loader)
	{
		bLoaded = _bt_loader->loadRawHisBars(&buffer, stdCode, period, [](void* obj, WTSBarStruct* bars, uint32_t count) {
			std::string* buff = (std::string*)obj;
			buff->resize(sizeof(WTSBarStruct)*count);
			memcpy((void*)buff->c_str(), bars, sizeof(WTSBarStruct)*count);
		});
	}

	if(!bLoaded)
	{
		bLoaded = _his_dt_mgr.load_raw_bars(cInfo._exchg, cInfo._code, period, [&buffer](std::string& data) {
			buffer.swap(data);
		});

		if(!bLoaded)
		{
			WTSLogger::warn_f("Loading {} bars of {} via HisDtMgr failed", PERIOD_NAME[period], stdCode);
		}
	}

	if (buffer.empty())
		return false;

	uint32_t barcnt = buffer.size() / sizeof(WTSBarStruct);

	WTSBarStruct* firstBar = (WTSBarStruct*)buffer.data();
	if (barcnt > 0)
	{

		uint32_t sIdx = 0;
		uint32_t idx = barcnt - 1;
		uint32_t curCnt = (idx - sIdx + 1);

		std::vector<WTSBarStruct>* tempAy = new std::vector<WTSBarStruct>();
		tempAy->resize(curCnt);
		memcpy(tempAy->data(), &firstBar[sIdx], sizeof(WTSBarStruct)*curCnt);
		realCnt += curCnt;

		barsSections.emplace_back(tempAy);
	}

	if (realCnt > 0)
	{
		barsList->_bars.resize(realCnt);

		uint32_t curIdx = 0;
		for (auto it = barsSections.rbegin(); it != barsSections.rend(); it++)
		{
			std::vector<WTSBarStruct>* tempAy = *it;
			memcpy(barsList->_bars.data() + curIdx, tempAy->data(), tempAy->size()*sizeof(WTSBarStruct));
			curIdx += tempAy->size();
			delete tempAy;
		}
		barsList->_count = barsList->_bars.size();
		barsSections.clear();
	}

	WTSLogger::info_f("{} items of back {} data of {} cached", realCnt, PERIOD_NAME[period], stdCode);
	return true;
}